Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> is used to have a deterministic output of the toString
* method of the union type since this output is used in tests.
*/
static final Comparator<JSType> ALPHA = new Comparator<JSType>() {
public int compare(JSType t1, JSType t2) {
return t1.toString().compareTo(t2.toString());
}
};
// A flag set on enum definition tree nodes
public static final int ENUMDECL = 1;
public static final int NOT_ENUMDECL = 0;
final JSTypeRegistry registry;
JSType(JSTypeRegistry registry) {
this.registry = registry;
}
/**
* Utility method for less verbose code.
*/
JSType getNativeType(JSTypeNative typeId) {
return registry.getNativeType(typeId);
}
/**
* Gets the docInfo for this type. By default, documentation cannot be
* attached to arbitrary types. This must be overridden for
* programmer-defined types.
*/
public JSDocInfo getJSDocInfo() {
return null;
}
/**
* Returns a user meaningful label for the JSType instance. For example,
* Functions and Enums will return their declaration name (if they have one).
* Some types will not have a meaningful display name. Calls to
* hasDisplayName() will return true IFF getDisplayName() will return null
* or a zero length string.
*
* @return the display name of the type, or null if one is not available
*/
public String getDisplayName() {
return null;
}
/**
* @return true if the JSType has a user meaningful label.
*/
public boolean hasDisplayName() {
String displayName = getDisplayName();
return false;
}
boolean isTheObjectType() {
return false;
}
public boolean isStringValueType() {
return false;
}
/**
* Tests whether the type is a string (value or Object).
* @return {@code this <: (String, string)}
*/
public final boolean isString() {
return this.isSubtype(
getNativeType(JSTypeNative.STRING_VALUE_OR_OBJECT_TYPE));
}
/**
* Tests whether the type is a number (value or Object).
* @return {@code this <: (Number, number)}
*/
public final boolean isNumber() {
return this.isSubtype(
getNativeType(JSTypeNative.NUMBER_VALUE_OR_OBJECT_TYPE));
}
public boolean isArrayType() {
return false;
}
public boolean isBooleanObjectType() {
return false;
}
public boolean isBooleanValueType() {
return false;
}
public boolean isRegexpType() {
return false;
}
public boolean isDateType() {
return false;
}
public boolean isNullType() {
return false;
}
public boolean isVoidType() {
return false;
}
public boolean isAllType() {
return false;
}
public boolean isUnknownType() {
return false;
}
public boolean isCheckedUnknownType() {
return false;
}
public boolean isUnionType() {
return false;
}
public boolean isFunctionType() {
return false;
}
public boolean isEnumElementType() {
return false;
}
public boolean isEnumType() {
return false;
}
boolean isNamedType() {
return false;
}
public boolean isRecordType() {
return false;
}
public boolean isTemplateType() {
return false;
}
/**
* Tests whether this type is an {@code Object}, or any subtype thereof.
* @return {@code
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>} always succeed (such as
* {@code undefined} compared to {@code null})</li>
* <li>{@link TernaryValue#FALSE} if the comparison of values of
* {@code this} type and {@code that} always fails (such as
* {@code undefined} compared to {@code number})</li>
* <li>{@link TernaryValue#UNKNOWN} if the comparison can succeed or
* fail depending on the concrete values</li>
* </ul>
*/
public TernaryValue testForEquality(JSType that) {
return testForEqualityHelper(this, that);
}
TernaryValue testForEqualityHelper(JSType aType, JSType bType) {
if (bType.isAllType() || bType.isUnknownType() ||
bType.isNoResolvedType() ||
aType.isAllType() || aType.isUnknownType() ||
aType.isNoResolvedType()) {
return UNKNOWN;
}
boolean aIsEmpty = aType.isEmptyType();
boolean bIsEmpty = bType.isEmptyType();
if (aIsEmpty || bIsEmpty) {
if (aIsEmpty && bIsEmpty) {
return TernaryValue.TRUE;
} else {
return UNKNOWN;
}
}
if (aType.isFunctionType() || bType.isFunctionType()) {
JSType otherType = aType.isFunctionType() ? bType : aType;
// In theory, functions are comparable to anything except
// null/undefined. For example, on FF3:
// function() {} == 'function () {\n}'
// In practice, how a function serializes to a string is
// implementation-dependent, so it does not really make sense to test
// for equality with a string.
JSType meet = otherType.getGreatestSubtype(
getNativeType(JSTypeNative.OBJECT_TYPE));
if (meet.isNoType() || meet.isNoObjectType()) {
return TernaryValue.FALSE;
} else {
return TernaryValue.UNKNOWN;
}
}
if (bType.isEnumElementType() || bType.isUnionType()) {
return bType.testForEquality(aType);
}
return null;
}
/**
* Tests whether {@code this} and {@code that} are meaningfully
* comparable using shallow comparison. By meaningfully, we mean compatible
* types that are not rejected by step 1 of the definition of the Strict
* Equality Comparison Algorithm (11.9.6, page 56–57) of the
* ECMA-262 specification.<p>
*/
public final boolean canTestForShallowEqualityWith(JSType that) {
return this.isSubtype(that) || that.isSubtype(this);
}
/**
* Tests whether this type is nullable.
*/
public boolean isNullable() {
return this.isSubtype(getNativeType(JSTypeNative.NULL_TYPE));
}
/**
* Gets the least supertype of {@code this} and {@code that}.
* The least supertype is the join (∨) or supremum of both types in the
* type lattice.<p>
* Examples:
* <ul>
* <li>{@code number ∨ *} = {@code *}</li>
* <li>{@code number ∨ Object} = {@code (number, Object)}</li>
* <li>{@code Number ∨ Object} = {@code Object}</li>
* </ul>
* @return {@code this ∨ that}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
*/
public JSType getLeastSupertype(JSType that) {
if (that.isUnionType()) {
// Union types have their own implementation of getLeastSupertype.
return that.getLeastSupertype(this);
}
return getLeastSupertype(this, that);
}
/**
* A generic implementation meant to be used as a helper for common
* getLeastSupertype implementations.
*/
static JSType getLeastSupertype(JSType thisType, JSType thatType) {
boolean areEquivalent = thisType.isEquivalentTo(thatType);
return areEquivalent ? thisType :
filterNoResolvedType(
thisType.registry.createUnionType(thisType, thatType));
}
/**
* Gets the greatest subtype of {@code this} and {@code that}.
* The greatest subtype is the meet (∧) or infimum of both types in the
* type lattice.<p>
* Examples
* <ul>
* <li>{@code Number ∧ Any} = {@code Any}</li>
* <li>{@code number ∧ Object} = {@code Any}</li>
* <li>{@code Number ∧ Object} = {@code Number}</li>
* </ul>
* @return {@code this ∨ that}
*/
public JSType getGreatestSubtype(JSType that) {
if (that.isRecordType()) {
// Record types have their own implementation of getGreatestSubtype.
return that.getGreatestSubtype(this);
}
return getGreatestSubtype(this, that);
}
/**
* A generic implementation meant to be used as a helper for common
* getGreatestSubtype implementations.
*/
static JSType getGreatestSubtype(JSType thisType, JSType thatType) {
if (thisType.isEquivalentTo(thatType)) {
return thisType;
} else if (thisType.isUnknownType() || thatType.isUnknownType()) {
// The greatest subtype with any unknown type is the universal
// unknown type, unless the two types are equal.
return thisType.isEquivalentTo(thatType) ? thisType :
thisType.getNativeType(JSTypeNative.UNKNOWN_TYPE);
} else if (thisType.isSubtype(thatType)) {
return filterNoResolvedType(thisType);
} else if (thatType.isSubtype(thisType)) {
return filterNoResolvedType(thatType);
} else if (thisType.isUnionType()) {
return ((UnionType) thisType).meet(thatType);
} else if (thatType.isUnionType()) {
return ((UnionType) thatType).meet(thisType);
} else if (thisType.isObject() && thatType.isObject()) {
return thisType.getNativeType(JSTypeNative.NO_OBJECT_TYPE);
}
return thisType.getNativeType(JSTypeNative.NO_TYPE);
}
/**
* When computing infimums, we may get a situation like
* inf(Type1, Type2)
* where both types are unresolved, so they're technically
* subtypes of one another.
*
* If this happens, filter them down to NoResolvedType.
*/
static JSType filterNoResolvedType(JSType type) {
if (type.isNoResolvedType()) {
// inf(UnresolvedType1, UnresolvedType2) needs to resolve
// to the base unresolved type, so that the relation is symmetric.
return type.getNativeType(JSTypeNative.NO_RESOLVED_TYPE);
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> else if (type instanceof UnionType) {
UnionType unionType = (UnionType) type;
boolean needsFiltering = false;
for (JSType alt : unionType.getAlternates()) {
if (alt.isNoResolvedType()) {
needsFiltering = true;
break;
}
}
if (needsFiltering) {
UnionTypeBuilder builder = new UnionTypeBuilder(type.registry);
for (JSType alt : unionType.getAlternates()) {
if (!alt.isNoResolvedType()) {
builder.addAlternate(alt);
}
}
return builder.build();
}
}
return type;
}
/**
* Computes the restricted type of this type knowing that the
* {@code ToBoolean} predicate has a specific value. For more information
* about the {@code ToBoolean} predicate, see
* {@link #getPossibleToBooleanOutcomes}.
*
* @param outcome the value of the {@code ToBoolean} predicate
*
* @return the restricted type, or the Any Type if the underlying type could
* not have yielded this ToBoolean value
*
* TODO(user): Move this method to the SemanticRAI and use the visit
* method of types to get the restricted type.
*/
public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) {
BooleanLiteralSet literals = getPossibleToBooleanOutcomes();
if (literals.contains(outcome)) {
return this;
} else {
return getNativeType(JSTypeNative.NO_TYPE);
}
}
/**
* Computes the set of possible outcomes of the {@code ToBoolean} predicate
* for this type. The {@code ToBoolean} predicate is defined by the ECMA-262
* standard, 3<sup>rd</sup> edition. Its behavior for simple types can be
* summarized by the following table:
* <table>
* <tr><th>type</th><th>result</th></tr>
* <tr><td>{@code undefined}</td><td>{false}</td></tr>
* <tr><td>{@code null}</td><td>{false}</td></tr>
* <tr><td>{@code boolean}</td><td>{true, false}</td></tr>
* <tr><td>{@code number}</td><td>{true, false}</td></tr>
* <tr><td>{@code string}</td><td>{true, false}</td></tr>
* <tr><td>{@code Object}</td><td>{true}</td></tr>
* </table>
* @return the set of boolean literals for this type
*/
public abstract BooleanLiteralSet getPossibleToBooleanOutcomes();
/**
* Computes the subset of {@code this} and {@code that} types if equality
* is observed. If a value {@code v1} of type {@code null} is equal to a value
* {@code v2} of type {@code (undefined,number)}, we can infer that the
* type of {@code v1} is {@code null} and the type of {@code v2} is
* {@code undefined}.
*
* @return a pair containing the restricted type of {@code this} as the first
* component and the restricted type of {@code that} as the second
* element. The returned pair is never {@code null} even though its
* components may be {@code null}
*/
public TypePair getTypesUnderEquality(JSType that) {
// unions types
if (that instanceof UnionType) {
TypePair p = that.getTypesUnderEquality(this);
return new TypePair(p.typeB, p.typeA);
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> }
// other types
switch (this.testForEquality(that)) {
case FALSE:
return new TypePair(null, null);
case TRUE:
case UNKNOWN:
return new TypePair(this, that);
}
// switch case is exhaustive
throw new IllegalStateException();
}
/**
* Computes the subset of {@code this} and {@code that} types if inequality
* is observed. If a value {@code v1} of type {@code number} is not equal to a
* value {@code v2} of type {@code (undefined,number)}, we can infer that the
* type of {@code v1} is {@code number} and the type of {@code v2} is
* {@code number} as well.
*
* @return a pair containing the restricted type of {@code this} as the first
* component and the restricted type of {@code that} as the second
* element. The returned pair is never {@code null} even though its
* components may be {@code null}
*/
public TypePair getTypesUnderInequality(JSType that) {
// unions types
if (that instanceof UnionType) {
TypePair p = that.getTypesUnderInequality(this);
return new TypePair(p.typeB, p.typeA);
}
// other types
switch (this.testForEquality(that)) {
case TRUE:
JSType noType = getNativeType(JSTypeNative.NO_TYPE);
return new TypePair(noType, noType);
case FALSE:
case UNKNOWN:
return new TypePair(this, that);
}
// switch case is exhaustive
throw new IllegalStateException();
}
/**
* Computes the subset of {@code this} and {@code that} types under shallow
* equality.
*
* @return a pair containing the restricted type of {@code this} as the first
* component and the restricted type of {@code that} as the second
* element. The returned pair is never {@code null} even though its
* components may be {@code null}.
*/
public TypePair getTypesUnderShallowEquality(JSType that) {
JSType commonType = getGreatestSubtype(that);
return new TypePair(commonType, commonType);
}
/**
* Computes the subset of {@code this} and {@code that} types under
* shallow inequality.
*
* @return A pair containing the restricted type of {@code this} as the first
* component and the restricted type of {@code that} as the second
* element. The returned pair is never {@code null} even though its
* components may be {@code null}
*/
public TypePair getTypesUnderShallowInequality(JSType that) {
// union types
if (that instanceof UnionType) {
TypePair p = that.getTypesUnderShallowInequality(this);
return new TypePair(p.typeB, p.typeA);
}
// Other types.
// There are only two types whose shallow inequality is deterministically
// true -- null and undefined. We can just enumerate them.
if (this.isNullType() && that.isNullType() ||
this.isVoidType() && that.isVoidType()) {
return new TypePair(null, null);
} else {
return new TypePair(this, that);
}
}
/**
* If this is a union type, returns a union type that does not include
* the null or undefined type.
*/
public JSType restrictByNotNullOrUndefined() {
return this;
}
/**
* Checks whether {@code this} is a subtype of
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> }
return false;
}
/**
* Visit this type with the given visitor.
* @see com.google.javascript.rhino.jstype.Visitor
* @return the value returned by the visitor
*/
public abstract <T> T visit(Visitor<T> visitor);
/**
* Force this type to resolve, even if the registry is in a lazy
* resolving mode.
* @see #resolve
*/
public final JSType forceResolve(ErrorReporter t, StaticScope<JSType> scope) {
ResolveMode oldResolveMode = registry.getResolveMode();
registry.setResolveMode(ResolveMode.IMMEDIATE);
JSType result = resolve(t, scope);
registry.setResolveMode(oldResolveMode);
return result;
}
/**
* Resolve this type in the given scope.
*
* The returned value must be equal to {@code this}, as defined by
* {@link #isEquivalentTo}. It may or may not be the same object. This method
* may modify the internal state of {@code this}, as long as it does
* so in a way that preserves Object equality.
*
* For efficiency, we should only resolve a type once per compilation job.
* For incremental compilations, one compilation job may need the
* artifacts from a previous generation, so we will eventually need
* a generational flag instead of a boolean one.
*/
public final JSType resolve(ErrorReporter t, StaticScope<JSType> scope) {
if (resolved) {
// TODO(nicksantos): Check to see if resolve() looped back on itself.
// Preconditions.checkNotNull(resolveResult);
if (resolveResult == null) {
return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
return resolveResult;
}
resolved = true;
resolveResult = resolveInternal(t, scope);
resolveResult.setResolvedTypeInternal(resolveResult);
return resolveResult;
}
/**
* @see #resolve
*/
abstract JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope);
void setResolvedTypeInternal(JSType type) {
resolveResult = type;
resolved = true;
}
/** Whether the type has been resolved. */
public final boolean isResolved() {
return resolved;
}
/** Clears the resolved field. */
public final void clearResolved() {
resolved = false;
resolveResult = null;
}
/**
* A null-safe resolve.
* @see #resolve
*/
static final JSType safeResolve(
JSType type, ErrorReporter t, StaticScope<JSType> scope) {
return type == null ? null : type.resolve(t, scope);
}
/**
* Certain types have constraints on them at resolution-time.
* For example, a type in an {@code @extends} annotation must be an
* object. Clients should inject a validator that emits a warning
* if the type does not validate, and return false.
*/
public boolean setValidator(Predicate<JSType> validator) {
return validator.apply(this);
}
public static class TypePair {
public final JSType typeA;
public final JSType typeB;
public TypePair(JSType typeA, JSType typeB) {
this.typeA = typeA;
this.typeB = typeB;
}
}
/**
* A hash code function for diagnosing complicated issues
* around type-identity.
*/
public String toDebugHashCodeString() {
return "{" + this.hashCode() + "}";
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>);
return informed;
}
private FlowScope caseInstanceOf(Node left, Node right, FlowScope blindScope,
boolean outcome) {
JSType leftType = getTypeIfRefinable(left, blindScope);
if (leftType == null) {
return blindScope;
}
JSType rightType = right.getJSType();
ObjectType targetType =
typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE);
if (rightType instanceof FunctionType) {
targetType = (FunctionType) rightType;
}
Visitor<JSType> visitor;
if (outcome) {
visitor = new RestrictByTrueInstanceOfResultVisitor(targetType);
} else {
visitor = new RestrictByFalseInstanceOfResultVisitor(targetType);
}
JSType restrictedLeftType = leftType.visit(visitor);
if (restrictedLeftType != null && !restrictedLeftType.equals(leftType)) {
FlowScope informed = blindScope.createChildFlowScope();
declareNameInScope(informed, left, restrictedLeftType);
return informed;
}
return blindScope;
}
/**
* Given 'property in object', ensures that the object has the property in the
* informed scope by defining it as a qualified name if the object type lacks
* the property and it's not in the blind scope.
* @param object The node of the right-side of the in.
* @param propertyName The string of the left-side of the in.
*/
private FlowScope caseIn(Node object, String propertyName, FlowScope blindScope) {
JSType jsType = object.getJSType();
jsType = this.getRestrictedWithoutNull(jsType);
jsType = this.getRestrictedWithoutUndefined(jsType);
boolean hasProperty = false;
ObjectType objectType = ObjectType.cast(jsType);
if (objectType != null) {
hasProperty = objectType.hasProperty(propertyName);
}
if (!hasProperty) {
String qualifiedName = object.getQualifiedName();
if (qualifiedName != null) {
String propertyQualifiedName = qualifiedName + "." + propertyName;
if (blindScope.getSlot(propertyQualifiedName) == null) {
FlowScope informed = blindScope.createChildFlowScope();
JSType unknownType = typeRegistry.getNativeType(
JSTypeNative.UNKNOWN_TYPE);
informed.inferQualifiedSlot(
propertyQualifiedName, unknownType, unknownType);
return informed;
}
}
}
return blindScope;
}
/**
* @see SemanticReverseAbstractInterpreter#caseInstanceOf
*/
private class RestrictByTrueInstanceOfResultVisitor
extends RestrictByTrueTypeOfResultVisitor {
private final ObjectType target;
RestrictByTrueInstanceOfResultVisitor(ObjectType target) {
this.target = target;
}
@Override
protected JSType caseTopType(JSType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseUnknownType() {
if (target instanceof FunctionType) {
FunctionType funcTarget = (FunctionType) target;
if (funcTarget.hasInstanceType()) {
return funcTarget.getInstanceType();
}
}
return getNativeType(UNKNOWN_TYPE);
}
@Override
public JSType caseObjectType(ObjectType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseUnionType(UnionType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseFunctionType(FunctionType type) {
return caseObjectType(type);
}
private JSType applyCommonRestriction(JSType type) {
if (target.isUnknownType()) {
return type;
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) {
// If there are two typed declarations of the same variable, that
// is an error and the second declaration is ignored, except in the
// case of native types. A null input type means that the declaration
// was made in TypedScopeCreator#createInitialScope and is a
// native type.
if (var.input == null) {
n.setJSType(varType);
if (parent.getType() == Token.VAR) {
if (n.getFirstChild() != null) {
n.getFirstChild().setJSType(varType);
}
} else {
Preconditions.checkState(parent.getType() == Token.FUNCTION);
parent.setJSType(varType);
}
} else {
// Always warn about duplicates if the overridden type does not
// match the original type.
//
// If the types match, suppress the warning iff there was a @suppress
// tag, or if the original declaration was a stub.
if (!(allowDupe ||
var.getParentNode().getType() == Token.EXPR_RESULT) ||
!newType.equals(varType)) {
if (shouldReport) {
compiler.report(
JSError.make(sourceName, n, DUP_VAR_DECLARATION,
variableName, newType.toString(), var.getInputName(),
String.valueOf(var.nameNode.getLineno()),
varType.toString()));
}
}
}
}
}
/**
* Expect that all properties on interfaces that this type implements are
* implemented.
*/
void expectAllInterfacePropertiesImplemented(FunctionType type) {
ObjectType instance = type.getInstanceType();
for (ObjectType implemented : type.getAllImplementedInterfaces()) {
if (implemented.getImplicitPrototype() != null) {
for (String prop :
implemented.getImplicitPrototype().getOwnPropertyNames()) {
if (!instance.hasProperty(prop)) {
Node source = type.getSource();
Preconditions.checkNotNull(source);
String sourceName = (String) source.getProp(Node.SOURCENAME_PROP);
sourceName = sourceName == null ? "" : sourceName;
if (shouldReport) {
compiler.report(JSError.make(sourceName, source,
INTERFACE_METHOD_NOT_IMPLEMENTED,
prop, implemented.toString(), instance.toString()));
}
registerMismatch(instance, implemented);
}
}
}
}
}
/**
* Report a type mismatch
*/
private void mismatch(NodeTraversal t, Node n,
String msg, JSType found, JSType required) {
mismatch(t.getSourceName(), n, msg, found, required);
}
private void mismatch(NodeTraversal t, Node n,
String msg, JSType found, JSTypeNative required) {
mismatch(t, n, msg, found, getNativeType(required));
}
private void mismatch(String sourceName, Node n,
String msg, JSType found, JSType required) {
registerMismatch(found, required);
if (shouldReport) {
compiler.report(
JSError.make(sourceName, n, TYPE_MISMATCH_WARNING,
formatFoundRequired(msg, found, required)));
}
}
private void registerMismatch(JSType found, JSType required) {
// Don't register a mismatch for differences in null or undefined or if the
// code didn't downcast.
found = found.restrictByNotNullOrUndefined();
required = required.restrictByNotNullOrUndefined();
if (found.canAssignTo(required) || required.canAssignTo(found)) {
return;
}
mismatches.add(
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> and analyze to track
// down why it happens. This is not critical and will be resolved over
// time as the type checker is extended.
return getNativeType(UNKNOWN_TYPE);
} else {
return jsType;
}
}
private JSType getNativeType(JSTypeNative typeId) {
return typeRegistry.getNativeType(typeId);
}
/**
* Signals that the first type and the second type have been
* used interchangeably.
*
* Type-based optimizations should take this into account
* so that they don't wreck code with type warnings.
*/
static class TypeMismatch {
final JSType typeA;
final JSType typeB;
/**
* It's the responsibility of the class that creates the
* {@code TypeMismatch} to ensure that {@code a} and {@code b} are
* non-matching types.
*/
TypeMismatch(JSType a, JSType b) {
this.typeA = a;
this.typeB = b;
}
@Override public boolean equals(Object object) {
if (object instanceof TypeMismatch) {
TypeMismatch that = (TypeMismatch) object;
return (that.typeA.equals(this.typeA) && that.typeB.equals(this.typeB))
|| (that.typeB.equals(this.typeA) && that.typeA.equals(this.typeB));
}
return false;
}
@Override public int hashCode() {
return Objects.hashCode(typeA, typeB);
}
@Override public String toString() {
return "(" + typeA + ", " + typeB + ")";
}
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
/**
* For functions with function(this: T, ...) and T as arguments, type inference
* will set the type of this on a function literal argument to the actual type
* of T.
*
*/
package com.google.javascript.rhino.jstype;
public class TemplateType extends ProxyObjectType {
private static final long serialVersionUID = 1L;
private final String name;
TemplateType(JSTypeRegistry registry, String name) {
super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE));
this.name = name;
}
@Override
public String getReferenceName() {
return name;
}
@Override
public String toString() {
return name;
}
@Override
public boolean isTemplateType() {
return true;
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.common.collect.ImmutableMap;
import com.google.javascript.rhino.Node;
/**
* A builder for record types.
*
*/
public class RecordTypeBuilder {
private boolean isEmpty = true;
private final JSTypeRegistry registry;
private final ImmutableMap.Builder<String, RecordProperty> properties =
ImmutableMap.builder();
public RecordTypeBuilder(JSTypeRegistry registry) {
this.registry = registry;
}
/**
* Adds a property with the given name and type to the record type.
* @param name the name of the new property
* @param type the JSType of the new property
* @param propertyNode the node that holds this property definition
* @return The builder itself for chaining purposes.
*/
public RecordTypeBuilder addProperty(String name, JSType type, Node
propertyNode) {
isEmpty = false;
properties.put(name, new RecordProperty(type, propertyNode));
return this;
}
/**
* Creates a record.
* @return The record type.
*/
public JSType build() {
// If we have an empty record, simply return the object type.
if (isEmpty) {
return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE);
}
return registry.createRecordType(properties.build());
}
static class RecordProperty {
private final JSType type;
private final Node propertyNode;
RecordProperty(JSType type, Node propertyNode) {
this.type = type;
this.propertyNode = propertyNode;
}
public JSType getType() {
return type;
}
public Node getPropertyNode() {
return propertyNode;
}
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
/**
* Boolean type.
*/
public class BooleanType extends ValueType {
private static final long serialVersionUID = 1L;
BooleanType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isNullable() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isUnknownType() || that.isSubtype(
getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN)) ||
that.isObject()) {
return UNKNOWN;
}
return FALSE;
}
@Override
public boolean isBooleanValueType() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
// TODO(user): Revisit this for ES4, which is stricter.
return true;
}
@Override
public JSType autoboxesTo() {
return getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE);
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "boolean";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseBoolean
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> MessageFormat("line {0} of {1}");
/**
* Create a pass that overrides define constants.
*
* TODO(nicksantos): Write a builder to help JSCompiler induce
* {@code replacements} from command-line flags
*
* @param replacements A hash table of names of defines to their replacements.
* All replacements <b>must</b> be literals.
*/
ProcessDefines(AbstractCompiler compiler, Map<String, Node> replacements) {
this.compiler = compiler;
dominantReplacements = replacements;
}
/**
* Injects a pre-computed global namespace, so that the same namespace
* can be re-used for multiple check passes. Returns {@code this} for
* easy chaining.
*/
ProcessDefines injectNamespace(GlobalNamespace namespace) {
this.namespace = namespace;
return this;
}
public void process(Node externs, Node root) {
if (namespace == null) {
namespace = new GlobalNamespace(compiler, root);
}
overrideDefines(collectDefines(root, namespace));
}
private void overrideDefines(Map<String, DefineInfo> allDefines) {
boolean changed = false;
for (Map.Entry<String, DefineInfo> def : allDefines.entrySet()) {
String defineName = def.getKey();
DefineInfo info = def.getValue();
Node inputValue = dominantReplacements.get(defineName);
Node finalValue = inputValue != null ?
inputValue : info.getLastValue();
if (finalValue != info.initialValue) {
info.initialValueParent.replaceChild(
info.initialValue, finalValue.cloneTree());
compiler.addToDebugLog("Overriding @define variable " + defineName);
changed = changed ||
finalValue.getType() != info.initialValue.getType() ||
!finalValue.isEquivalentTo(info.initialValue);
}
}
if (changed) {
compiler.reportCodeChange();
}
Set<String> unusedReplacements = dominantReplacements.keySet();
unusedReplacements.removeAll(allDefines.keySet());
unusedReplacements.removeAll(KNOWN_DEFINES);
for (String unknownDefine : unusedReplacements) {
compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine));
}
}
private static String format(MessageFormat format, Object... params) {
return format.format(params);
}
/**
* Only defines of literal number, string, or boolean are supported.
*/
private boolean isValidDefineType(JSTypeExpression expression) {
JSType type = expression.evaluate(null, compiler.getTypeRegistry());
return !type.isUnknownType() && type.isSubtype(
compiler.getTypeRegistry().getNativeType(
JSTypeNative.NUMBER_STRING_BOOLEAN));
}
/**
* Finds all defines, and creates a {@link DefineInfo} data structure for
* each one.
* @return A map of {@link DefineInfo} structures, keyed by name.
*/
private Map<String, DefineInfo> collectDefines(Node root,
GlobalNamespace namespace) {
// Find all the global names with a @define annotation
List<Name> allDefines = Lists.newArrayList();
for (Name name : namespace.getNameIndex().values()) {
if (name.docInfo != null && name.docInfo.isDefine()) {
// Process defines should not depend on check types being enabled,
// so we look for the JSDoc instead of the inferred type.
if (isValidDefineType(name.docInfo.getType())) {
allDefines.add(name);
} else {
JSError error = JSError.make
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>> {
static final DiagnosticType TEMPLATE_TYPE_NOT_OBJECT_TYPE =
DiagnosticType.error(
"JSC_TEMPLATE_TYPE_NOT_OBJECT_TYPE",
"The template type must be an object type");
static final DiagnosticType TEMPLATE_TYPE_OF_THIS_EXPECTED =
DiagnosticType.error(
"JSC_TEMPLATE_TYPE_OF_THIS_EXPECTED",
"A function type with the template type as the type of this must be a " +
"parameter type");
static final DiagnosticType FUNCTION_LITERAL_UNDEFINED_THIS =
DiagnosticType.warning(
"JSC_FUNCTION_LITERAL_UNDEFINED_THIS",
"Function literal argument refers to undefined this argument");
static final DiagnosticType FUNCTION_LITERAL_UNREAD_THIS =
DiagnosticType.warning(
"JSC_FUNCTION_LITERAL_UNREAD_THIS",
"Function literal argument does not refer to bound this argument");
private final AbstractCompiler compiler;
private final JSTypeRegistry registry;
private final ReverseAbstractInterpreter reverseInterpreter;
private final Scope syntacticScope;
private final FlowScope functionScope;
private final FlowScope bottomScope;
private final Map<String, AssertionFunctionSpec> assertionFunctionsMap;
/**
* Local variables that do not belong to this scope, but are assigned
* in this scope.
*/
private final Multimap<Scope, Var> assignedOuterLocalVars =
HashMultimap.create();
/**
* Vars that we should not map out type flow for.
*/
private final Set<String> unflowableVarNames = Sets.newHashSet();
TypeInference(AbstractCompiler compiler, ControlFlowGraph<Node> cfg,
ReverseAbstractInterpreter reverseInterpreter,
Scope functionScope,
Map<String, AssertionFunctionSpec> assertionFunctionsMap) {
this(compiler, cfg, reverseInterpreter, functionScope,
assertionFunctionsMap, ImmutableSet.<Var>of());
}
/**
* @param unflowableVars Do not do infer flow on the types of these vars.
* @param assertionFunctionsMap
*/
// TODO(nicksantos): Create a builder for this class.
TypeInference(AbstractCompiler compiler, ControlFlowGraph<Node> cfg,
ReverseAbstractInterpreter reverseInterpreter,
Scope functionScope,
Map<String, AssertionFunctionSpec> assertionFunctionsMap,
Collection<Var> unflowableVars) {
super(cfg, new LinkedFlowScope.FlowScopeJoinOp());
this.compiler = compiler;
this.registry = compiler.getTypeRegistry();
this.reverseInterpreter = reverseInterpreter;
this.syntacticScope = functionScope;
this.functionScope = LinkedFlowScope.createEntryLattice(functionScope);
this.assertionFunctionsMap = assertionFunctionsMap;
for (Var unflowableVar : unflowableVars) {
String name = unflowableVar.getName();
if (functionScope.getVar(name) == unflowableVar) {
this.unflowableVarNames.add(name);
}
}
Iterator<Var> varIt = functionScope.getVars();
while (varIt.hasNext()) {
Var var = varIt.next();
if (this.unflowableVarNames.contains(var.getName())) {
continue;
}
// For each local variable declared with the VAR keyword, the entry
// type is VOID.
if (var.getParentNode() != null &&
var.getType() == null && // no declared type
var.getParentNode().getType() == Token.VAR &&
!var.isExtern()) {
this.functionScope.inferSlotType(
var.getName(), getNativeType(VOID_TYPE));
}
}
this.bottomScope = LinkedFlowScope.createEntryLattice(
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
case Token.MOD:
case Token.BITAND:
case Token.BITXOR:
case Token.BITOR:
case Token.MUL:
case Token.SUB:
case Token.DEC:
case Token.INC:
case Token.BITNOT:
scope = traverseChildren(n, scope);
n.setJSType(getNativeType(NUMBER_TYPE));
break;
case Token.LP:
case Token.GET_REF:
scope = traverse(n.getFirstChild(), scope);
n.setJSType(getJSType(n.getFirstChild()));
break;
case Token.COMMA:
scope = traverseChildren(n, scope);
n.setJSType(getJSType(n.getLastChild()));
break;
case Token.TYPEOF:
scope = traverseChildren(n, scope);
n.setJSType(getNativeType(STRING_TYPE));
break;
case Token.LT:
case Token.LE:
case Token.GT:
case Token.GE:
case Token.NOT:
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE:
case Token.INSTANCEOF:
case Token.IN:
scope = traverseChildren(n, scope);
n.setJSType(getNativeType(BOOLEAN_TYPE));
break;
case Token.GETELEM:
scope = traverseGetElem(n, scope);
break;
case Token.EXPR_RESULT:
scope = traverseChildren(n, scope);
if (n.getFirstChild().getType() == Token.GETPROP) {
ensurePropertyDeclared(n.getFirstChild());
}
break;
case Token.SWITCH:
scope = traverse(n.getFirstChild(), scope);
break;
case Token.VAR:
case Token.RETURN:
case Token.THROW:
scope = traverseChildren(n, scope);
break;
case Token.CATCH:
scope = traverseCatch(n, scope);
break;
}
if (n.getType() != Token.FUNCTION) {
JSDocInfo info = n.getJSDocInfo();
if (info != null && info.hasType()) {
JSType castType = info.getType().evaluate(syntacticScope, registry);
// A stubbed type cast on a qualified name should take
// effect for all subsequent accesses of that name,
// so treat it the same as an assign to that name.
if (n.isQualifiedName() &&
n.getParent().getType() == Token.EXPR_RESULT) {
updateScopeForTypeChange(scope, n, n.getJSType(), castType);
}
n.setJSType(castType);
}
}
return scope;
}
/**
* Any value can be thrown, so it's really impossible to determine the type
* of a CATCH param. Treat it as the UNKNOWN type.
*/
private FlowScope traverseCatch(Node n, FlowScope scope) {
Node name = n.getFirstChild();
JSType type = getNativeType(JSTypeNative.UNKNOWN_TYPE);
name.setJSType(type);
redeclare(scope, name.getString(), type);
return scope;
}
private FlowScope traverseAssign(Node n, FlowScope scope) {
Node left = n.getFirstChild();
Node right = n.getLastChild();
scope = traverseChildren(n, scope);
JSType leftType = left.getJSType();
JSType rightType = getJSType(right);
n.setJSType(rightType);
updateScopeForTypeChange(scope, left, leftType, rightType);
return scope;
}
/**
* Updates the scope
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> according to the result of a type change, like
* an assignment or a type cast.
*/
private void updateScopeForTypeChange(
FlowScope scope, Node left, JSType leftType, JSType resultType) {
Preconditions.checkNotNull(resultType);
switch (left.getType()) {
case Token.NAME:
String varName = left.getString();
Var var = syntacticScope.getVar(varName);
if (var != null && var.isLocal() && var.getScope() != syntacticScope) {
assignedOuterLocalVars.put(var.getScope(), var);
}
// When looking at VAR initializers for declared VARs, we trust
// the declared type over the type it's being initialized to.
// This has two purposes:
// 1) We avoid re-declaring declared variables so that built-in
// types defined in externs are not redeclared.
// 2) When there's a lexical closure like
// /** @type {?string} */ var x = null;
// function f() { x = 'xyz'; }
// the inference will ignore the lexical closure,
// which is just wrong. This bug needs to be fixed eventually.
boolean isVarDeclaration = left.hasChildren();
if (!isVarDeclaration || var == null || var.isTypeInferred()) {
redeclare(scope, varName, resultType);
}
left.setJSType(isVarDeclaration || leftType == null ?
resultType : null);
if (var != null && var.isTypeInferred()) {
JSType oldType = var.getType();
var.setType(oldType == null ?
resultType : oldType.getLeastSupertype(resultType));
}
break;
case Token.GETPROP:
String qualifiedName = left.getQualifiedName();
if (qualifiedName != null) {
scope.inferQualifiedSlot(qualifiedName,
leftType == null ? getNativeType(UNKNOWN_TYPE) : leftType,
resultType);
}
left.setJSType(resultType);
ensurePropertyDefined(left, resultType);
break;
}
}
/**
* Defines a property if the property has not been defined yet.
*/
private void ensurePropertyDefined(Node getprop, JSType rightType) {
String propName = getprop.getLastChild().getString();
JSType nodeType = getJSType(getprop.getFirstChild());
ObjectType objectType = ObjectType.cast(
nodeType.restrictByNotNullOrUndefined());
if (objectType == null) {
registry.registerPropertyOnType(propName, nodeType);
} else {
if (ensurePropertyDeclaredHelper(getprop, objectType)) {
return;
}
if (!objectType.isPropertyTypeDeclared(propName)) {
// We do not want a "stray" assign to define an inferred property
// for every object of this type in the program. So we use a heuristic
// approach to determine whether to infer the propery.
//
// 1) If the property is already defined, join it with the previously
// inferred type.
// 2) If this isn't an instance object, define it.
// 3) If the property of an object is being assigned in the constructor,
// define it.
// 4) If this is a stub, define it.
// 5) Otherwise, do not define the type, but declare it in the registry
// so that we can use it for missing property checks.
if (objectType.hasProperty(propName) ||
!objectType.isInstanceType()) {
if ("prototype".equals(propName)) {
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> objectType.defineDeclaredProperty(propName, rightType, false, getprop);
} else {
objectType.defineInferredProperty(propName, rightType, false, getprop);
}
} else {
if (getprop.getFirstChild().getType() == Token.THIS &&
getJSType(syntacticScope.getRootNode()).isConstructor()) {
objectType.defineInferredProperty(propName, rightType, false, getprop);
} else {
registry.registerPropertyOnType(propName, objectType);
}
}
}
}
}
/**
* Defines a declared property if it has not been defined yet.
*
* This handles the case where a property is declared on an object where
* the object type is inferred, and so the object type will not
* be known in {@code TypedScopeCreator}.
*/
private void ensurePropertyDeclared(Node getprop) {
ObjectType ownerType = ObjectType.cast(
getJSType(getprop.getFirstChild()).restrictByNotNullOrUndefined());
if (ownerType != null) {
ensurePropertyDeclaredHelper(getprop, ownerType);
}
}
/**
* Declares a property on its owner, if necessary.
* @return True if a property was declared.
*/
private boolean ensurePropertyDeclaredHelper(
Node getprop, ObjectType objectType) {
String propName = getprop.getLastChild().getString();
String qName = getprop.getQualifiedName();
if (qName != null) {
Var var = syntacticScope.getVar(qName);
if (var != null && !var.isTypeInferred()) {
// Handle normal declarations that could not be addressed earlier.
if (propName.equals("prototype") ||
// Handle prototype declarations that could not be addressed earlier.
(!objectType.hasOwnProperty(propName) &&
(!objectType.isInstanceType() ||
(var.isExtern() && !objectType.isNativeObjectType())))) {
return objectType.defineDeclaredProperty(
propName, var.getType(), var.isExtern(), getprop);
}
}
}
return false;
}
private FlowScope traverseName(Node n, FlowScope scope) {
String varName = n.getString();
Node value = n.getFirstChild();
JSType type = n.getJSType();
if (value != null) {
scope = traverse(value, scope);
updateScopeForTypeChange(scope, n, n.getJSType() /* could be null */,
getJSType(value));
return scope;
} else {
StaticSlot<JSType> var = scope.getSlot(varName);
if (var != null) {
// There are two situations where we don't want to use type information
// from the scope, even if we have it.
// 1) The var is escaped in a weird way, e.g.,
// function f() { var x = 3; function g() { x = null } (x); }
boolean isInferred = var.isTypeInferred();
boolean unflowable =
isInferred && unflowableVarNames.contains(varName);
// 2) We're reading type information from another scope for an
// inferred variable.
// var t = null; function f() { (t); }
boolean nonLocalInferredSlot =
isInferred &&
syntacticScope.getParent() != null &&
var == syntacticScope.getParent().getSlot(varName);
if (!unflowable && !nonLocalInferredSlot) {
type = var.getType();
if (type == null) {
type = getNativeType(UNKNOWN_TYPE);
}
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
}
n.setJSType(type);
return scope;
}
/** Traverse each element of the array. */
private FlowScope traverseArrayLiteral(Node n, FlowScope scope) {
scope = traverseChildren(n, scope);
n.setJSType(getNativeType(ARRAY_TYPE));
return scope;
}
private FlowScope traverseObjectLiteral(Node n, FlowScope scope) {
JSType type = n.getJSType();
Preconditions.checkNotNull(type);
for (Node name = n.getFirstChild(); name != null; name = name.getNext()) {
scope = traverse(name.getFirstChild(), scope);
}
// Object literals can be reflected on other types, or changed with
// type casts.
// See CodingConvention#getObjectLiteralCase and goog.object.reflect.
// Ignore these types of literals.
// TODO(nicksantos): There should be an "anonymous object" type that
// we can check for here.
ObjectType objectType = ObjectType.cast(type);
if (objectType == null) {
return scope;
}
boolean hasLendsName = n.getJSDocInfo() != null &&
n.getJSDocInfo().getLendsName() != null;
if (objectType.hasReferenceName() && !hasLendsName) {
return scope;
}
for (Node name = n.getFirstChild(); name != null;
name = name.getNext()) {
Node value = name.getFirstChild();
String memberName = NodeUtil.getObjectLitKeyName(name);
if (memberName != null) {
JSType rawValueType = name.getFirstChild().getJSType();
JSType valueType = NodeUtil.getObjectLitKeyTypeFromValueType(
name, rawValueType);
if (valueType == null) {
valueType = getNativeType(UNKNOWN_TYPE);
}
objectType.defineInferredProperty(memberName, valueType, false, name);
} else {
n.setJSType(getNativeType(UNKNOWN_TYPE));
}
}
return scope;
}
private FlowScope traverseAdd(Node n, FlowScope scope) {
Node left = n.getFirstChild();
Node right = left.getNext();
scope = traverseChildren(n, scope);
JSType leftType = left.getJSType();
JSType rightType = right.getJSType();
JSType type = getNativeType(UNKNOWN_TYPE);
if (leftType != null && rightType != null) {
boolean leftIsUnknown = leftType.isUnknownType();
boolean rightIsUnknown = rightType.isUnknownType();
if (leftIsUnknown && rightIsUnknown) {
type = getNativeType(UNKNOWN_TYPE);
} else if ((!leftIsUnknown && leftType.isString()) ||
(!rightIsUnknown && rightType.isString())) {
type = getNativeType(STRING_TYPE);
} else if (leftIsUnknown || rightIsUnknown) {
type = getNativeType(UNKNOWN_TYPE);
} else if (isAddedAsNumber(leftType) && isAddedAsNumber(rightType)) {
type = getNativeType(NUMBER_TYPE);
} else {
type = registry.createUnionType(STRING_TYPE, NUMBER_TYPE);
}
}
n.setJSType(type);
if (n.getType() == Token.ASSIGN_ADD) {
updateScopeForTypeChange(scope, left, leftType, type);
}
return scope;
}
private boolean isAddedAsNumber(JSType type) {
return type.isSubtype(registry.createUnionType(VOID_TYPE, NULL_TYPE,
NUMBER_VALUE_OR
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>_OBJECT_TYPE, BOOLEAN_TYPE, BOOLEAN_OBJECT_TYPE));
}
private FlowScope traverseHook(Node n, FlowScope scope) {
Node condition = n.getFirstChild();
Node trueNode = condition.getNext();
Node falseNode = n.getLastChild();
// verify the condition
scope = traverse(condition, scope);
// reverse abstract interpret the condition to produce two new scopes
FlowScope trueScope = reverseInterpreter.
getPreciserScopeKnowingConditionOutcome(
condition, scope, true);
FlowScope falseScope = reverseInterpreter.
getPreciserScopeKnowingConditionOutcome(
condition, scope, false);
// traverse the true node with the trueScope
traverse(trueNode, trueScope.createChildFlowScope());
// traverse the false node with the falseScope
traverse(falseNode, falseScope.createChildFlowScope());
// meet true and false nodes' types and assign
JSType trueType = trueNode.getJSType();
JSType falseType = falseNode.getJSType();
if (trueType != null && falseType != null) {
n.setJSType(trueType.getLeastSupertype(falseType));
} else {
n.setJSType(null);
}
return scope.createChildFlowScope();
}
private FlowScope traverseCall(Node n, FlowScope scope) {
scope = traverseChildren(n, scope);
Node left = n.getFirstChild();
JSType functionType = getJSType(left).restrictByNotNullOrUndefined();
if (functionType != null) {
if (functionType instanceof FunctionType) {
FunctionType fnType = (FunctionType) functionType;
n.setJSType(fnType.getReturnType());
updateTypeOfParametersOnClosure(n, fnType);
updateTypeOfThisOnClosure(n, fnType);
} else if (functionType.equals(getNativeType(CHECKED_UNKNOWN_TYPE))) {
n.setJSType(getNativeType(CHECKED_UNKNOWN_TYPE));
}
}
scope = tightenTypesAfterAssertions(scope, n);
return scope;
}
private FlowScope tightenTypesAfterAssertions(FlowScope scope,
Node callNode) {
Node left = callNode.getFirstChild();
Node firstParam = left.getNext();
AssertionFunctionSpec assertionFunctionSpec =
assertionFunctionsMap.get(left.getQualifiedName());
if (assertionFunctionSpec == null || firstParam == null) {
return scope;
}
Node assertedNode = assertionFunctionSpec.getAssertedParam(firstParam);
if (assertedNode == null) {
return scope;
}
JSTypeNative assertedType = assertionFunctionSpec.getAssertedType();
String assertedNodeName = assertedNode.getQualifiedName();
// Handle assertions that enforce expressions evaluate to true.
if (assertedType == null) {
if (assertedNodeName != null) {
JSType type = getJSType(assertedNode);
JSType narrowed = type.restrictByNotNullOrUndefined();
if (type != narrowed) {
scope = scope.createChildFlowScope();
redeclare(scope, assertedNodeName, narrowed);
callNode.setJSType(narrowed);
}
} else if (assertedNode.getType() == Token.AND ||
assertedNode.getType() == Token.OR) {
BooleanOutcomePair conditionOutcomes =
traverseWithinShortCircuitingBinOp(assertedNode, scope);
scope = reverseInterpreter.getPreciserScopeKnowingConditionOutcome(
assertedNode, conditionOutcomes.getOutcomeFlowScope(
assertedNode.getType(), true), true);
}
} else if (assert
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> (jArgument.getType() == Token.FUNCTION &&
jArgumentType instanceof FunctionType) {
if (iArgumentType != null &&
// null and undefined get filtered out above.
!iArgumentType.isNoType()) {
// If it's an function expression, update the type of this
// using the actual type of T.
FunctionType jArgumentFnType = (FunctionType) jArgumentType;
if (jArgumentFnType.getTypeOfThis().isUnknownType()) {
// The new type will be picked up when we traverse the inner
// function.
jArgument.setJSType(
registry.createFunctionTypeWithNewThisType(
jArgumentFnType, (ObjectType) iArgumentType));
}
// Warn if the anonymous function literal does not reference this.
if (!NodeUtil.referencesThis(
NodeUtil.getFunctionBody(jArgument))) {
compiler.report(JSError.make(NodeUtil.getSourceName(n), n,
FUNCTION_LITERAL_UNREAD_THIS));
}
} else {
// Warn if the anonymous function literal references this.
if (NodeUtil.referencesThis(
NodeUtil.getFunctionBody(jArgument))) {
compiler.report(JSError.make(NodeUtil.getSourceName(n), n,
FUNCTION_LITERAL_UNDEFINED_THIS));
}
}
}
// TODO(user): Add code to TypeCheck to check that the
// types of the arguments match.
}
}
j++;
}
if (!foundTemplateTypeOfThisParameter) {
compiler.report(JSError.make(NodeUtil.getSourceName(n), n,
TEMPLATE_TYPE_OF_THIS_EXPECTED));
return;
}
}
i++;
}
}
private FlowScope traverseNew(Node n, FlowScope scope) {
Node constructor = n.getFirstChild();
scope = traverse(constructor, scope);
JSType constructorType = constructor.getJSType();
JSType type = null;
if (constructorType != null) {
constructorType = constructorType.restrictByNotNullOrUndefined();
if (constructorType.isUnknownType()) {
type = getNativeType(UNKNOWN_TYPE);
} else if (constructorType instanceof FunctionType) {
FunctionType ct = (FunctionType) constructorType;
if (ct.isConstructor()) {
type = ct.getInstanceType();
}
}
}
n.setJSType(type);
for (Node arg = constructor.getNext(); arg != null; arg = arg.getNext()) {
scope = traverse(arg, scope);
}
return scope;
}
private BooleanOutcomePair traverseAnd(Node n, FlowScope scope) {
return traverseShortCircuitingBinOp(n, scope, true);
}
private FlowScope traverseChildren(Node n, FlowScope scope) {
for (Node el = n.getFirstChild(); el != null; el = el.getNext()) {
scope = traverse(el, scope);
}
return scope;
}
private FlowScope traverseGetElem(Node n, FlowScope scope) {
scope = traverseChildren(n, scope);
ObjectType objType = ObjectType.cast(
getJSType(n.getFirstChild()).restrictByNotNullOrUndefined());
if (objType != null) {
JSType type = objType.getParameterType();
if (type != null) {
n.setJSType(type);
}
}
return dereferencePointer(n.getFirstChild(), scope);
}
private FlowScope traverseGetProp(Node n, FlowScope scope) {
Node objNode = n.getFirstChild();
Node property = n.getLastChild();
scope = traverseChildren(n, scope);
n.set
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>JSType(
getPropertyType(
objNode.getJSType(), property.getString(), n, scope));
return dereferencePointer(n.getFirstChild(), scope);
}
/**
* If we access a property of a symbol, then that symbol is not
* null or undefined.
*/
private FlowScope dereferencePointer(Node n, FlowScope scope) {
if (n.getType() == Token.NAME) {
JSType type = getJSType(n);
JSType narrowed = type.restrictByNotNullOrUndefined();
if (type != narrowed) {
scope = scope.createChildFlowScope();
redeclare(scope, n.getString(), narrowed);
}
}
return scope;
}
private JSType getPropertyType(JSType objType, String propName,
Node n, FlowScope scope) {
// Scopes sometimes contain inferred type info about qualified names.
String qualifiedName = n.getQualifiedName();
StaticSlot<JSType> var = scope.getSlot(qualifiedName);
if (var != null) {
JSType varType = var.getType();
if (varType != null) {
if (varType.equals(getNativeType(UNKNOWN_TYPE)) &&
var != syntacticScope.getSlot(qualifiedName)) {
// If the type of this qualified name has been checked in this scope,
// then use CHECKED_UNKNOWN_TYPE instead to indicate that.
return getNativeType(CHECKED_UNKNOWN_TYPE);
} else {
return varType;
}
}
}
JSType propertyType = null;
if (objType != null) {
propertyType = objType.findPropertyType(propName);
}
if ((propertyType == null || propertyType.isUnknownType()) &&
qualifiedName != null) {
// If we find this node in the registry, then we can infer its type.
ObjectType regType = ObjectType.cast(registry.getType(qualifiedName));
if (regType != null) {
propertyType = regType.getConstructor();
}
}
return propertyType;
}
private BooleanOutcomePair traverseOr(Node n, FlowScope scope) {
return traverseShortCircuitingBinOp(n, scope, false);
}
private BooleanOutcomePair traverseShortCircuitingBinOp(
Node n, FlowScope scope, boolean condition) {
Node left = n.getFirstChild();
Node right = n.getLastChild();
// type the left node
BooleanOutcomePair leftLiterals =
traverseWithinShortCircuitingBinOp(left,
scope.createChildFlowScope());
JSType leftType = left.getJSType();
// reverse abstract interpret the left node to produce the correct
// scope in which to verify the right node
FlowScope rightScope = reverseInterpreter.
getPreciserScopeKnowingConditionOutcome(
left, leftLiterals.getOutcomeFlowScope(left.getType(), condition),
condition);
// type the right node
BooleanOutcomePair rightLiterals =
traverseWithinShortCircuitingBinOp(
right, rightScope.createChildFlowScope());
JSType rightType = right.getJSType();
JSType type;
BooleanOutcomePair literals;
if (leftType != null && rightType != null) {
leftType = leftType.getRestrictedTypeGivenToBooleanOutcome(!condition);
if (leftLiterals.toBooleanOutcomes ==
BooleanLiteralSet.get(!condition)) {
// Use the restricted left type, since the right side never gets
// evaluated.
type = leftType;
literals = leftLiterals;
} else {
// Use the join of the restricted left type knowing the outcome of the
// ToBoolean predicate and of the right type
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> FlowScope leftScope, FlowScope rightScope) {
this.toBooleanOutcomes = toBooleanOutcomes;
this.booleanValues = booleanValues;
this.leftScope = leftScope;
this.rightScope = rightScope;
}
/**
* Gets the safe estimated scope without knowing if all of the subexpressions
* will be evaluated.
*/
FlowScope getJoinedFlowScope() {
if (joinedScope == null) {
if (leftScope == rightScope) {
joinedScope = rightScope;
} else {
joinedScope = join(leftScope, rightScope);
}
}
return joinedScope;
}
/**
* Gets the outcome scope if we do know the outcome of the entire
* expression.
*/
FlowScope getOutcomeFlowScope(int nodeType, boolean outcome) {
if (nodeType == Token.AND && outcome ||
nodeType == Token.OR && !outcome) {
// We know that the whole expression must have executed.
return rightScope;
} else {
return getJoinedFlowScope();
}
}
}
private BooleanOutcomePair newBooleanOutcomePair(
JSType jsType, FlowScope flowScope) {
if (jsType == null) {
return new BooleanOutcomePair(
BooleanLiteralSet.BOTH, BooleanLiteralSet.BOTH, flowScope, flowScope);
}
return new BooleanOutcomePair(jsType.getPossibleToBooleanOutcomes(),
registry.getNativeType(BOOLEAN_TYPE).isSubtype(jsType) ?
BooleanLiteralSet.BOTH : BooleanLiteralSet.EMPTY,
flowScope, flowScope);
}
private void redeclare(FlowScope scope, String varName, JSType varType) {
if (varType == null) {
varType = getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
if (unflowableVarNames.contains(varName)) {
return;
}
scope.inferSlotType(varName, varType);
}
/**
* This method gets the JSType from the Node argument and verifies that it is
* present.
*/
private JSType getJSType(Node n) {
JSType jsType = n.getJSType();
if (jsType == null) {
// TODO(nicksantos): This branch indicates a compiler bug, not worthy of
// halting the compilation but we should log this and analyze to track
// down why it happens. This is not critical and will be resolved over
// time as the type checker is extended.
return getNativeType(UNKNOWN_TYPE);
} else {
return jsType;
}
}
private JSType getNativeType(JSTypeNative typeId) {
return registry.getNativeType(typeId);
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
/**
* String type.
*/
public final class StringType extends ValueType {
private static final long serialVersionUID = 1L;
StringType(JSTypeRegistry registry) {
super(registry);
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isUnknownType() || that.isSubtype(
getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) {
return UNKNOWN;
}
return FALSE;
}
@Override
public boolean isStringValueType() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
// TODO(user): Revisit this for ES4, which is stricter.
return true;
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "string";
}
@Override
public JSType autoboxesTo() {
return getNativeType(JSTypeNative.STRING_OBJECT_TYPE);
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseStringType();
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> JSType evaluate(StaticScope<JSType> scope, JSTypeRegistry registry) {
return registry.createFromTypeNodes(root, sourceName, scope,
root.getBooleanProp(Node.BRACELESS_TYPE));
}
@Override
public boolean equals(Object other) {
return other instanceof JSTypeExpression &&
((JSTypeExpression) other).root.checkTreeEqualsSilent(root);
}
@Override
public int hashCode() {
return root.toStringTree().hashCode();
}
Node getRoot() {
return root;
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Nick Santos
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.testing.BaseJSTypeTestCase;
/**
* Tests for FunctionTypes.
* @author nicksantos@google.com (Nick Santos)
*/
public class FunctionTypeTest extends BaseJSTypeTestCase {
public void testDefaultReturnType() {
FunctionType f = new FunctionBuilder(registry).build();
assertEquals(UNKNOWN_TYPE, f.getReturnType());
}
public void testSupAndInfOfReturnTypes() {
FunctionType retString = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters())
.withInferredReturnType(STRING_TYPE).build();
FunctionType retNumber = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters())
.withReturnType(NUMBER_TYPE).build();
assertLeastSupertype(
"function (): (number|string)", retString, retNumber);
assertGreatestSubtype(
"function (): None", retString, retNumber);
assertTrue(retString.isReturnTypeInferred());
assertFalse(retNumber.isReturnTypeInferred());
assertTrue(
((FunctionType) retString.getLeastSupertype(retNumber))
.isReturnTypeInferred());
assertTrue(
((FunctionType) retString.getGreatestSubtype(retString))
.isReturnTypeInferred());
}
public void testSupAndInfOfReturnTypesWithDifferentParams() {
FunctionType retString = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters(NUMBER_TYPE))
.withInferredReturnType(STRING_TYPE).build();
FunctionType retNumber = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters())
.withReturnType(NUMBER_TYPE).build();
assertLeastSupertype(
"Function", retString, retNumber);
assertGreatestSubtype(
"function
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> (...[*]): None", retString, retNumber);
}
public void testSupAndInfWithDifferentParams() {
FunctionType retString = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters(NUMBER_TYPE))
.withReturnType(STRING_TYPE).build();
FunctionType retNumber = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters(STRING_TYPE))
.withReturnType(NUMBER_TYPE).build();
assertLeastSupertype(
"Function", retString, retNumber);
assertGreatestSubtype(
"function (...[*]): None", retString, retNumber);
}
public void testSupAndInfWithDifferentThisTypes() {
FunctionType retString = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters())
.withTypeOfThis(OBJECT_TYPE)
.withReturnType(STRING_TYPE).build();
FunctionType retNumber = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters())
.withTypeOfThis(DATE_TYPE)
.withReturnType(NUMBER_TYPE).build();
assertLeastSupertype(
"function (this:Object): (number|string)", retString, retNumber);
assertGreatestSubtype(
"function (this:Date): None", retString, retNumber);
}
public void testSupAndInfWithDifferentThisTypes2() {
FunctionType retString = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters())
.withTypeOfThis(ARRAY_TYPE)
.withReturnType(STRING_TYPE).build();
FunctionType retNumber = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters())
.withTypeOfThis(DATE_TYPE)
.withReturnType(NUMBER_TYPE).build();
assertLeastSupertype(
"function (this:Object): (number|string)", retString, retNumber);
assertGreatestSubtype(
"function (this:NoObject): None", retString, retNumber);
}
public void testSupAndInfOfReturnTypesWithNumOfParams() {
FunctionType twoNumbers = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters(NUMBER_TYPE, NUMBER_TYPE))
.withReturnType(BOOLEAN_TYPE).build();
FunctionType oneNumber = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters(NUMBER_TYPE))
.withReturnType(BOOLEAN_TYPE).build();
assertLeastSupertype(
"Function", twoNumbers, oneNumber);
assertGreatestSubtype(
"function (...[*]): None", twoNumbers, oneNumber);
}
public void testSubtypeWithInterfaceThisType() {
FunctionType iface = registry.createInterfaceType("I", null);
FunctionType ifaceReturnBoolean = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters())
.withTypeOfThis(iface.getInstanceType())
.withReturnType(BOOLEAN_TYPE).build();
FunctionType objReturnBoolean = new FunctionBuilder(registry)
.withParamsNode(registry.createParameters())
.withTypeOfThis(OBJECT_TYPE)
.withReturnType(BOOLEAN_TYPE).build();
assertTrue(objReturnBoolean.canAssignTo(ifaceReturnBoolean));
}
public void testCtorWithPrototypeSet() {
FunctionType ctor = registry.createConstructorType(
"Foo", null, null, null);
assertFalse(ctor.getInstanceType().isUnknownType());
ctor.defineDeclaredProperty("prototype", UNKNOWN_TYPE, false, null);
assertTrue(ctor.getInstanceType().isUnknownType());
}
public void testEmptyFunctionTypes() {
assertTrue(LEAST_FUNCTION_TYPE.isEmptyType());
assertFalse(GREATEST_FUNCTION_
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
/**
* An object type with a declared default index type.
*
* For example, <code>Object.<number, string></code> can take only numbers as
* keys.
*
*/
final class IndexedType extends ProxyObjectType {
private static final long serialVersionUID = 1L;
final JSType indexType;
IndexedType(
JSTypeRegistry registry, ObjectType objectType, JSType indexType) {
super(registry, objectType);
this.indexType = indexType;
}
@Override
public JSType getIndexType() {
return indexType;
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.Node;
/**
* A builder class for function and arrow types.
*
* If you need to build an interface constructor,
* use {@link JSTypeRegistry#createInterfaceType}.
*
* @author nicksantos@google.com (Nick Santos)
*/
public final class FunctionBuilder {
private final JSTypeRegistry registry;
private String name = null;
private Node sourceNode = null;
private Node parametersNode = null;
private JSType returnType = null;
private ObjectType typeOfThis = null;
private String templateTypeName = null;
private boolean inferredReturnType = false;
private boolean isConstructor = false;
private boolean isNativeType = false;
public FunctionBuilder(JSTypeRegistry registry) {
this.registry = registry;
}
/** Set the name of the function type. */
public FunctionBuilder withName(String name) {
this.name = name;
return this;
}
/** Set the source node of the function type. */
public FunctionBuilder withSourceNode(Node sourceNode) {
this.sourceNode = sourceNode;
return this;
}
/** Set the parameters of the function type from a FunctionParamBuilder. */
public FunctionBuilder withParams(FunctionParamBuilder params) {
this.parametersNode = params.build();
return this;
}
/**
* Set the parameters of the function type with a specially-formatted node.
*/
public FunctionBuilder withParamsNode(Node parametersNode) {
this.parametersNode = parametersNode;
return this;
}
/** Set the return type. */
public FunctionBuilder withReturnType(JSType returnType) {
this.returnType = returnType;
return this;
}
/** Set the return type and whether it's inferred. */
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> public FunctionBuilder withReturnType(JSType returnType, boolean inferred) {
this.returnType = returnType;
this.inferredReturnType = inferred;
return this;
}
/** Sets an inferred return type. */
public FunctionBuilder withInferredReturnType(JSType returnType) {
this.returnType = returnType;
this.inferredReturnType = true;
return this;
}
/** Set the "this" type. */
public FunctionBuilder withTypeOfThis(ObjectType typeOfThis) {
this.typeOfThis = typeOfThis;
return this;
}
/** Set the template name. */
public FunctionBuilder withTemplateName(String templateTypeName) {
this.templateTypeName = templateTypeName;
return this;
}
/** Make this a constructor. */
public FunctionBuilder forConstructor() {
this.isConstructor = true;
return this;
}
/** Set whether this is a constructor. */
public FunctionBuilder setIsConstructor(boolean isConstructor) {
this.isConstructor = isConstructor;
return this;
}
/** Make this a native type. */
FunctionBuilder forNativeType() {
this.isNativeType = true;
return this;
}
/** Copies all the information from another function type. */
public FunctionBuilder copyFromOtherFunction(FunctionType otherType) {
this.name = otherType.getReferenceName();
this.sourceNode = otherType.getSource();
this.parametersNode = otherType.getParametersNode();
this.returnType = otherType.getReturnType();
this.typeOfThis = otherType.getTypeOfThis();
this.templateTypeName = otherType.getTemplateTypeName();
this.isConstructor = otherType.isConstructor();
this.isNativeType = otherType.isNativeObjectType();
return this;
}
/** Construct a new function type. */
public FunctionType build() {
return new FunctionType(registry, name, sourceNode,
new ArrowType(registry, parametersNode, returnType, inferredReturnType),
typeOfThis, templateTypeName, isConstructor, isNativeType);
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.Node;
import java.util.Set;
/**
* The {@code Unknown} type.
*/
public class UnknownType extends ObjectType {
private static final long serialVersionUID = 1L;
// See the explanation of checked unknown types in JSTypeNative.
private final boolean isChecked;
UnknownType(JSTypeRegistry registry, boolean isChecked) {
super(registry);
this.isChecked = isChecked;
}
@Override
public boolean isUnknownType() {
return true;
}
@Override
public boolean isCheckedUnknownType() {
return isChecked;
}
@Override
public boolean canAssignTo(JSType that) {
return true;
}
@Override
public boolean canBeCalled() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public TernaryValue testForEquality(JSType that) {
return UNKNOWN;
}
@Override
public boolean isNullable() {
return true;
}
@Override
public boolean isSubtype(JSType that) {
return true;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseUnknownType();
}
@Override
public String toString() {
return getReferenceName();
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, boolean inExterns, Node propertyNode)
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Nick Santos
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.common.base.Preconditions;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.Node;
/**
* An {@code UnresolvedType} is a reference to some type expression.
* This provides a convenient mechanism for implementing forward
* references to types; a {@code UnresolvedType} can be used as a
* placeholder until its reference is resolved.
*
* The {@code UnresolvedType} will behave like an opaque unknown type.
* When its {@code #resolve} method is called, it will return the underlying
* type. The underlying type can resolve to any JS type.<p>
*
* @author nicksantos@google.com (Nick Santos)
*/
class UnresolvedTypeExpression extends UnknownType {
private static final long serialVersionUID = 1L;
private final Node typeExpr;
private final String sourceName;
/**
* If true, don't warn about unresolveable type names.
*
* NOTE(nicksantos): A lot of third-party code doesn't use our type syntax.
* They have code like
* {@code @return} the bus.
* and they clearly don't mean that "the" is a type. In these cases, we're
* forgiving and try to guess whether or not "the" is a type when it's not
* clear.
*/
private boolean forgiving = false;
/**
* Create a named type based on the reference.
*/
UnresolvedTypeExpression(JSTypeRegistry registry, Node typeExpr,
String sourceName, boolean forgiving) {
super(registry, false);
Preconditions.checkNotNull(typeExpr);
this.typeExpr = typeExpr;
this.sourceName = sourceName;
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> this.forgiving = forgiving;
}
/**
* Resolve the referenced type within the enclosing scope.
*/
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> enclosing) {
return registry.createFromTypeNodes(typeExpr, sourceName, enclosing,
forgiving);
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.TRUE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
/**
* Void type whose only element is the {@code undefined} value.
*/
public class VoidType extends ValueType {
private static final long serialVersionUID = 1L;
VoidType(JSTypeRegistry registry) {
super(registry);
}
@Override
public JSType restrictByNotNullOrUndefined() {
return registry.getNativeType(JSTypeNative.NO_TYPE);
}
@Override
public TernaryValue testForEquality(JSType that) {
if (UNKNOWN.equals(super.testForEquality(that))) {
return UNKNOWN;
}
if (that.isSubtype(this) ||
that.isSubtype(getNativeType(JSTypeNative.NULL_TYPE))) {
return TRUE;
}
return FALSE;
}
@Override
public boolean matchesNumberContext() {
return false;
}
@Override
public boolean matchesObjectContext() {
return false;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean isVoidType() {
return true;
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "undefined";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.FALSE;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseVoidType();
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> summarized by the
* following table:
* <table>
* <tr><th>type</th><th>result</th></tr>
* <tr><td>{@code undefined}</td><td>"undefined"</td></tr>
* <tr><td>{@code null}</td><td>"object"</td></tr>
* <tr><td>{@code boolean}</td><td>"boolean"</td></tr>
* <tr><td>{@code number}</td><td>"number"</td></tr>
* <tr><td>{@code string}</td><td>"string"</td></tr>
* <tr><td>{@code Object} (which doesn't implement [[Call]])</td>
* <td>"object"</td></tr>
* <tr><td>{@code Object} (which implements [[Call]])</td>
* <td>"function"</td></tr>
* </table>
* @param type the type to restrict
* @param value A value known to be equal or not equal to the result of the
* {@code typeof} operation
* @param resultEqualsValue {@code true} if the {@code typeOf} result is known
* to equal {@code value}; {@code false} if it is known <em>not</em> to
* equal {@code value}
* @return the restricted type or null if no version of the type matches the
* restriction
*/
JSType getRestrictedByTypeOfResult(JSType type, String value,
boolean resultEqualsValue) {
if (type == null) {
if (resultEqualsValue) {
JSType result = getNativeTypeForTypeOf(value);
return result == null ? getNativeType(UNKNOWN_TYPE) : result;
} else {
return null;
}
}
return type.visit(
new RestrictByOneTypeOfResultVisitor(value, resultEqualsValue));
}
JSType getNativeType(JSTypeNative typeId) {
return typeRegistry.getNativeType(typeId);
}
/**
* If we definitely know what a type is based on the typeof result,
* return it. Otherwise, return null.
*
* The typeof operation in JS is poorly defined, and this function works
* for both the native typeof and goog.typeOf. It should not be made public,
* because its semantics are informally defined, and would be wrong in
* the general case.
*/
private JSType getNativeTypeForTypeOf(String value) {
if (value.equals("number")) {
return getNativeType(NUMBER_TYPE);
} else if (value.equals("boolean")) {
return getNativeType(BOOLEAN_TYPE);
} else if (value.equals("string")) {
return getNativeType(STRING_TYPE);
} else if (value.equals("undefined")) {
return getNativeType(VOID_TYPE);
} else if (value.equals("function")) {
return getNativeType(U2U_CONSTRUCTOR_TYPE);
} else {
return null;
}
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> type.isEnumType() predicate. Currently, not all enum types are
// implemented by the EnumClass, e.g. the unknown type and the any
// type. The types need to be defined by interfaces such that an
// implementation can implement multiple types interface.
EnumType valueEnumType = (EnumType) value.getJSType();
JSType valueEnumPrimitiveType =
valueEnumType.getElementsType().getPrimitiveType();
validator.expectCanAssignTo(t, value, valueEnumPrimitiveType,
primitiveType, "incompatible enum element types");
} else {
// The error condition is handled in TypedScopeCreator.
}
}
/**
* This predicate is used to determine if the node represents an expression
* that is a Reference according to JavaScript definitions.
*
* @param n The node being checked.
* @return true if the sub-tree n is a reference, false otherwise.
*/
private static boolean isReference(Node n) {
switch (n.getType()) {
case Token.GETELEM:
case Token.GETPROP:
case Token.NAME:
return true;
default:
return false;
}
}
/**
* This method gets the JSType from the Node argument and verifies that it is
* present.
*/
private JSType getJSType(Node n) {
JSType jsType = n.getJSType();
if (jsType == null) {
// TODO(nicksantos): This branch indicates a compiler bug, not worthy of
// halting the compilation but we should log this and analyze to track
// down why it happens. This is not critical and will be resolved over
// time as the type checker is extended.
return getNativeType(UNKNOWN_TYPE);
} else {
return jsType;
}
}
/**
* Gets the type of the node or {@code null} if the node's type is not a
* function.
*/
private FunctionType getFunctionType(Node n) {
JSType type = getJSType(n).restrictByNotNullOrUndefined();
if (type.isUnknownType()) {
return typeRegistry.getNativeFunctionType(U2U_CONSTRUCTOR_TYPE);
} else if (type instanceof FunctionType) {
return (FunctionType) type;
} else {
return null;
}
}
// TODO(nicksantos): TypeCheck should never be attaching types to nodes.
// All types should be attached by TypeInference. This is not true today
// for legacy reasons. There are a number of places where TypeInference
// doesn't attach a type, as a signal to TypeCheck that it needs to check
// that node's type.
/**
* Ensure that the given node has a type. If it does not have one,
* attach the UNKNOWN_TYPE.
*/
private void ensureTyped(NodeTraversal t, Node n) {
ensureTyped(t, n, getNativeType(UNKNOWN_TYPE));
}
private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) {
ensureTyped(t, n, getNativeType(type));
}
/**
* Enforces type casts, and ensures the node is typed.
*
* A cast in the way that we use it in JSDoc annotations never
* alters the generated code and therefore never can induce any runtime
* operation. What this means is that a 'cast' is really just a compile
* time constraint on the underlying value. In the future, we may add
* support for run-time casts for compiled tests.
*
* To ensure some shred of sanity, we enforce the notion that the
* type you are casting to may only meaningfully
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> be a narrower type
* than the underlying declared type. We also invalidate optimizations
* on bad type casts.
*
* @param t The traversal object needed to report errors.
* @param n The node getting a type assigned to it.
* @param type The type to be assigned.
*/
private void ensureTyped(NodeTraversal t, Node n, JSType type) {
// Make sure FUNCTION nodes always get function type.
Preconditions.checkState(n.getType() != Token.FUNCTION ||
type instanceof FunctionType ||
type.isUnknownType());
JSDocInfo info = n.getJSDocInfo();
if (info != null) {
if (info.hasType()) {
JSType infoType = info.getType().evaluate(t.getScope(), typeRegistry);
validator.expectCanCast(t, n, infoType, type);
type = infoType;
}
if (info.isImplicitCast() && !inExterns) {
String propName = n.getType() == Token.GETPROP ?
n.getLastChild().getString() : "(missing)";
compiler.report(
t.makeError(n, ILLEGAL_IMPLICIT_CAST, propName));
}
}
if (n.getJSType() == null) {
n.setJSType(type);
}
}
/**
* Returns the percentage of nodes typed by the type checker.
* @return a number between 0.0 and 100.0
*/
double getTypedPercent() {
int total = nullCount + unknownCount + typedCount;
if (total == 0) {
return 0.0;
} else {
return (100.0 * typedCount) / total;
}
}
private JSType getNativeType(JSTypeNative typeId) {
return typeRegistry.getNativeType(typeId);
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
/**
* An unresolved type that was forward declared. So we know it exists,
* but that it wasn't pulled into this binary.
*
* In most cases, it behaves like a bottom type in the type lattice:
* no real type should be assigned to a NoResolvedType, but the
* NoResolvedType is a subtype of everything. In a few cases, it behaves
* like the unknown type: properties of this type are also NoResolved types,
* and comparisons to other types always have an unknown result.
*
* @author nicksantos@google.com (Nick Santos)
*/
class NoResolvedType extends NoType {
private static final long serialVersionUID = 1L;
NoResolvedType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isNoResolvedType() {
return true;
}
@Override
public boolean isNoType() {
return false;
}
@Override
public JSType getPropertyType(String propertyName) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
@Override
public boolean isSubtype(JSType that) {
if (JSType.isSubtype(this, that)) {
return true;
} else {
return !that.isNoType();
}
}
@Override
public String toString() {
return "NoResolvedType";
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.ErrorReporter;
/**
* Value types (null, void, number, boolean, string).
*/
abstract class ValueType extends JSType {
ValueType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isSubtype(JSType that) {
return JSType.isSubtype(this, that);
}
@Override
final JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
return this;
}
@Override
public boolean hasDisplayName() {
return true;
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>.
private boolean prettyPrint = false;
private static final int MAX_PRETTY_PRINTED_PROPERTIES = 4;
/**
* Creates an object type.
*
* @param className the name of the class. May be {@code null} to
* denote an anonymous class.
*
* @param implicitPrototype the implicit prototype
* (a.k.a. {@code [[Prototype]]}) as defined by ECMA-262. If the
* implicit prototype is {@code null} the implicit prototype will be
* set to the {@link JSTypeNative#OBJECT_TYPE}.
*/
PrototypeObjectType(JSTypeRegistry registry, String className,
ObjectType implicitPrototype) {
this(registry, className, implicitPrototype, false);
}
/**
* Creates an object type, allowing specification of the implicit prototype
* when creating native objects.
*/
PrototypeObjectType(JSTypeRegistry registry, String className,
ObjectType implicitPrototype, boolean nativeType) {
super(registry);
this.properties = Maps.newTreeMap();
this.className = className;
this.nativeType = nativeType;
if (nativeType || implicitPrototype != null) {
setImplicitPrototype(implicitPrototype);
} else {
setImplicitPrototype(
registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE));
}
}
/**
* Gets the number of properties of this object.
*/
@Override
public int getPropertiesCount() {
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype == null) {
return this.properties.size();
}
int localCount = 0;
for (String property : properties.keySet()) {
if (!implicitPrototype.hasProperty(property)) {
localCount++;
}
}
return implicitPrototype.getPropertiesCount() + localCount;
}
@Override
public boolean hasProperty(String propertyName) {
if (properties.get(propertyName) != null) {
return true;
}
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
return implicitPrototype.hasProperty(propertyName);
}
return false;
}
@Override
public boolean hasOwnProperty(String propertyName) {
return properties.get(propertyName) != null;
}
@Override
public Set<String> getOwnPropertyNames() {
return properties.keySet();
}
@Override
public boolean isPropertyTypeDeclared(String property) {
Property p = properties.get(property);
if (p == null) {
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
return implicitPrototype.isPropertyTypeDeclared(property);
}
// property does not exist
return false;
}
return !p.inferred;
}
@Override
void collectPropertyNames(Set<String> props) {
for (String prop : properties.keySet()) {
props.add(prop);
}
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
implicitPrototype.collectPropertyNames(props);
}
}
@Override
public boolean isPropertyTypeInferred(String property) {
Property p = properties.get(property);
if (p == null) {
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
return implicitPrototype.isPropertyTypeInferred(property);
}
// property does not exist
return false;
}
return p.inferred;
}
@Override
public JSType getPropertyType(String propertyName) {
Property p = properties.get(propertyName);
if (p != null) {
return p.type;
}
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype !=
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> null) {
return implicitPrototype.getPropertyType(propertyName);
}
return getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
@Override
public boolean isPropertyInExterns(String propertyName) {
Property p = properties.get(propertyName);
if (p != null) {
return p.inExterns;
}
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
return implicitPrototype.isPropertyInExterns(propertyName);
}
return false;
}
@Override
boolean defineProperty(String name, JSType type, boolean inferred,
boolean inExterns, Node propertyNode) {
if (hasOwnDeclaredProperty(name)) {
return false;
}
properties.put(name, new Property(type, inferred, inExterns, propertyNode));
return true;
}
@Override
public Node getPropertyNode(String propertyName) {
Property p = properties.get(propertyName);
if (p != null) {
return p.propertyNode;
}
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
return implicitPrototype.getPropertyNode(propertyName);
}
return null;
}
@Override
public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) {
Property p = properties.get(propertyName);
if (p != null) {
return p.docInfo;
}
return null;
}
@Override
public void setPropertyJSDocInfo(String propertyName, JSDocInfo info,
boolean inExterns) {
if (info != null) {
if (!properties.containsKey(propertyName)) {
// If docInfo was attached, but the type of the property
// was not defined anywhere, then we consider this an explicit
// declaration of the property.
defineInferredProperty(propertyName, getPropertyType(propertyName),
inExterns, null);
}
// The prototype property is not represented as a normal Property.
// We probably don't want to attach any JSDoc to it anyway.
Property property = properties.get(propertyName);
if (property != null) {
property.docInfo = info;
}
}
}
@Override
public boolean matchesNumberContext() {
return isNumberObjectType() || isDateType() || isBooleanObjectType() ||
isStringObjectType() || hasOverridenNativeProperty("valueOf");
}
@Override
public boolean matchesStringContext() {
return isTheObjectType() || isStringObjectType() || isDateType() ||
isRegexpType() || isArrayType() || isNumberObjectType() ||
isBooleanObjectType() || hasOverridenNativeProperty("toString");
}
/**
* Given the name of a native object property, checks whether the property is
* present on the object and different from the native one.
*/
private boolean hasOverridenNativeProperty(String propertyName) {
if (isNative()) {
return false;
}
JSType propertyType = getPropertyType(propertyName);
ObjectType nativeType =
this.isFunctionType() ?
registry.getNativeObjectType(JSTypeNative.FUNCTION_PROTOTYPE) :
registry.getNativeObjectType(JSTypeNative.OBJECT_PROTOTYPE);
JSType nativePropertyType = nativeType.getPropertyType(propertyName);
return propertyType != nativePropertyType;
}
@Override
public JSType unboxesTo() {
if (isStringObjectType()) {
return getNativeType(JSTypeNative.STRING_TYPE);
} else if (isBooleanObjectType()) {
return getNativeType(JSTypeNative.BOOLEAN_TYPE);
} else if (isNumberObjectType()) {
return getNativeType(JSTypeNative.NUMBER_TYPE
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import java.util.Set;
/**
* Object type.
*
* In JavaScript, all object types have properties, and each of those
* properties has a type. Property types may be DECLARED, INFERRED, or
* UNKNOWN.
*
* DECLARED properties have an explicit type annotation, as in:
* <code>
* /xx @type {number} x/
* Foo.prototype.bar = 1;
* </code>
* This property may only hold number values, and an assignment to any
* other type of value is an error.
*
* INFERRED properties do not have an explicit type annotation. Rather,
* we try to find all the possible types that this property can hold.
* <code>
* Foo.prototype.bar = 1;
* </code>
* If the programmer assigns other types of values to this property,
* the property will take on the union of all these types.
*
* UNKNOWN properties are properties on the UNKNOWN type. The UNKNOWN
* type has all properties, but we do not know whether they are
* declared or inferred.
*
*/
public abstract class ObjectType extends JSType {
private boolean visited;
private JSDocInfo docInfo = null;
private boolean unknown = true;
ObjectType(JSTypeRegistry registry) {
super(registry);
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> are comparable to everything but null/undefined
if (that.isSubtype(
getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) {
return UNKNOWN;
} else {
return FALSE;
}
}
/**
* Gets this object's constructor.
* @return this object's constructor or {@code null} if it is a native
* object (constructed natively v.s. by instantiation of a function)
*/
public abstract FunctionType getConstructor();
/**
* Gets the implicit prototype (a.k.a. the {@code [[Prototype]]} property).
*/
public abstract ObjectType getImplicitPrototype();
/**
* Defines a property whose type is synthesized (i.e. not inferred).
* @param propertyName the property's name
* @param type the type
* @param inExterns {@code true} if this property was defined in an externs
* file. TightenTypes assumes that any function passed to an externs
* property could be called, so setting this incorrectly could result
* in live code being removed.
* @param propertyNode the node corresponding to the declaration of property
* which might later be accessed using {@code getPropertyNode}.
*/
public final boolean defineDeclaredProperty(String propertyName,
JSType type, boolean inExterns, Node propertyNode) {
boolean result = defineProperty(propertyName, type, false, inExterns,
propertyNode);
// All property definitions go through this method
// or defineDeclaredProperty. Because the properties defined an an
// object can affect subtyping, it's slightly more efficient
// to register this after defining the property.
registry.registerPropertyOnType(propertyName, this);
return result;
}
/**
* Defines a property whose type is inferred.
* @param propertyName the property's name
* @param type the type
* @param inExterns {@code true} if this property was defined in an externs
* file. TightenTypes assumes that any function passed to an externs
* property could be called, so setting this incorrectly could result
* in live code being removed.
* @param propertyNode the node corresponding to the inferred definition of
* property that might later be accessed using {@code getPropertyNode}.
*/
public final boolean defineInferredProperty(String propertyName,
JSType type, boolean inExterns, Node propertyNode) {
if (hasProperty(propertyName)) {
JSType originalType = getPropertyType(propertyName);
type = originalType == null ? type :
originalType.getLeastSupertype(type);
}
boolean result = defineProperty(propertyName, type, true, inExterns,
propertyNode);
// All property definitions go through this method
// or defineDeclaredProperty. Because the properties defined an an
// object can affect subtyping, it's slightly more efficient
// to register this after defining the property.
registry.registerPropertyOnType(propertyName, this);
return result;
}
/**
* Defines a property.<p>
*
* For clarity, callers should prefer {@link #defineDeclaredProperty} and
* {@link #defineInferredProperty}.
*
* @param propertyName the property's name
* @param type the type
* @param inferred {@code true} if this property's type is inferred
* @param inExterns {@code true} if this property was defined in an externs
* file. TightenTypes assumes that any function passed to an externs
* property could be called, so setting this incorrectly could result
* in live code being removed.
* @param propertyNode the node that represents the definition of property.
* Depending
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> existing typing logic is hacky. Unresolved types should get processed
* in a more consistent way, but with the Rhino merge coming, there will be
* much that has to be changed.<p>
*
*/
class NamedType extends ProxyObjectType {
private static final long serialVersionUID = 1L;
private final String reference;
private final String sourceName;
private final int lineno;
private final int charno;
/**
* Validates the type resolution.
*/
private Predicate<JSType> validator;
/**
* If true, don't warn about unresolveable type names.
*
* NOTE(nicksantos): A lot of third-party code doesn't use our type syntax.
* They have code like
* {@code @return} the bus.
* and they clearly don't mean that "the" is a type. In these cases, we're
* forgiving and try to guess whether or not "the" is a type when it's not
* clear.
*/
private boolean forgiving = false;
/**
* Create a named type based on the reference.
*/
NamedType(JSTypeRegistry registry, String reference,
String sourceName, int lineno, int charno) {
super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE));
Preconditions.checkNotNull(reference);
this.reference = reference;
this.sourceName = sourceName;
this.lineno = lineno;
this.charno = charno;
}
@Override
void forgiveUnknownNames() {
forgiving = true;
}
/** Returns the type to which this refers (which is unknown if unresolved). */
public JSType getReferencedType() {
return getReferencedTypeInternal();
}
@Override
public String getReferenceName() {
return reference;
}
@Override
public String toString() {
return reference;
}
@Override
public boolean hasReferenceName() {
return true;
}
@Override
boolean isNamedType() {
return true;
}
@Override
public boolean isNominalType() {
return true;
}
/**
* Two named types are equivalent if they are the same {@code
* ObjectType} object. This is complicated by the fact that isEquivalent
* is sometimes called before we have a chance to resolve the type
* names.
*
* @return {@code true} iff {@code that} == {@code this} or {@code that}
* is a {@link NamedType} whose reference is the same as ours,
* or {@code that} is the type we reference.
*/
@Override
public boolean isEquivalentTo(JSType that) {
if (this == that) {
return true;
}
ObjectType objType = ObjectType.cast(that);
if (objType != null) {
return objType.isNominalType() &&
reference.equals(objType.getReferenceName());
}
return false;
}
@Override
public int hashCode() {
return reference.hashCode();
}
/**
* Resolve the referenced type within the enclosing scope.
*/
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> enclosing) {
// TODO(user): Investigate whether it is really necessary to keep two
// different mechanisms for resolving named types, and if so, which order
// makes more sense. Now, resolution via registry is first in order to
// avoid triggering the warnings built into the resolution via properties.
boolean resolved = resolveViaRegistry(t, enclosing);
if (detectImplicitPrototypeCycle()) {
handleTypeCycle(t);
}
if (resolved) {
super.
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>resolveInternal(t, enclosing);
return registry.isLastGeneration() ?
getReferencedType() : this;
}
resolveViaProperties(t, enclosing);
if (detectImplicitPrototypeCycle()) {
handleTypeCycle(t);
}
super.resolveInternal(t, enclosing);
return registry.isLastGeneration() ?
getReferencedType() : this;
}
/**
* Resolves a named type by looking it up in the registry.
* @return True if we resolved successfully.
*/
private boolean resolveViaRegistry(
ErrorReporter t, StaticScope<JSType> enclosing) {
JSType type = registry.getType(reference);
if (type != null) {
setReferencedAndResolvedType(type, t, enclosing);
return true;
}
return false;
}
/**
* Resolves a named type by looking up its first component in the scope, and
* subsequent components as properties. The scope must have been fully
* parsed and a symbol table constructed.
*/
private void resolveViaProperties(ErrorReporter t,
StaticScope<JSType> enclosing) {
JSType value = lookupViaProperties(t, enclosing);
// last component of the chain
if ((value instanceof FunctionType) &&
(value.isConstructor() || value.isInterface())) {
FunctionType functionType = (FunctionType) value;
setReferencedAndResolvedType(
functionType.getInstanceType(), t, enclosing);
} else if (value instanceof EnumType) {
setReferencedAndResolvedType(
((EnumType) value).getElementsType(), t, enclosing);
} else {
// We've been running into issues where people forward-declare
// non-named types. (This is legitimate...our dependency management
// code doubles as our forward-declaration code.)
//
// So if the type does resolve to an actual value, but it's not named,
// then don't respect the forward declaration.
handleUnresolvedType(t, value == null || value.isUnknownType());
}
}
/**
* Resolves a type by looking up its first component in the scope, and
* subsequent components as properties. The scope must have been fully
* parsed and a symbol table constructed.
* @return The type of the symbol, or null if the type could not be found.
*/
private JSType lookupViaProperties( ErrorReporter t,
StaticScope<JSType> enclosing) {
String[] componentNames = reference.split("\\.", -1);
if (componentNames[0].length() == 0) {
return null;
}
StaticSlot<JSType> slot = enclosing.getSlot(componentNames[0]);
if (slot == null) {
return null;
}
// If the first component has a type of 'Unknown', then any type
// names using it should be regarded as silently 'Unknown' rather than be
// noisy about it.
JSType slotType = slot.getType();
if (slotType == null || slotType.isAllType() || slotType.isNoType()) {
return null;
}
JSType value = getTypedefType(t, slot, componentNames[0]);
if (value == null) {
return null;
}
// resolving component by component
for (int i = 1; i < componentNames.length; i++) {
ObjectType parentClass = ObjectType.cast(value);
if (parentClass == null) {
return null;
}
if (componentNames[i].length() == 0) {
return null;
}
value = parentClass.getPropertyType(componentNames[i]);
}
return value;
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
private void setReferencedAndResolvedType(JSType type, ErrorReporter t,
StaticScope<JSType> enclosing) {
if (validator != null) {
validator.apply(type);
}
setReferencedType(type);
checkEnumElementCycle(t);
setResolvedTypeInternal(getReferencedType());
}
private void handleTypeCycle(ErrorReporter t) {
setReferencedType(
registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE));
t.warning("Cycle detected in inheritance chain of type " + reference,
sourceName, lineno, null, charno);
setResolvedTypeInternal(getReferencedType());
}
private void checkEnumElementCycle(ErrorReporter t) {
JSType referencedType = getReferencedType();
if (referencedType instanceof EnumElementType &&
((EnumElementType) referencedType).getPrimitiveType() == this) {
handleTypeCycle(t);
}
}
// Warns about this type being unresolved iff it's not a forward-declared
// type name.
private void handleUnresolvedType(
ErrorReporter t, boolean ignoreForwardReferencedTypes) {
if (registry.isLastGeneration()) {
boolean isForwardDeclared =
ignoreForwardReferencedTypes &&
registry.isForwardDeclaredType(reference);
boolean beForgiving = forgiving || isForwardDeclared;
if (!beForgiving && registry.isLastGeneration()) {
t.warning("Unknown type " + reference, sourceName, lineno, null,
charno);
} else {
if (isForwardDeclared) {
setReferencedType(
registry.getNativeObjectType(
JSTypeNative.NO_RESOLVED_TYPE));
} else {
setReferencedType(
registry.getNativeObjectType(
JSTypeNative.CHECKED_UNKNOWN_TYPE));
}
if (registry.isLastGeneration() && validator != null) {
validator.apply(getReferencedType());
}
}
setResolvedTypeInternal(getReferencedType());
} else {
setResolvedTypeInternal(this);
}
}
JSType getTypedefType(ErrorReporter t, StaticSlot<JSType> slot, String name) {
JSType type = slot.getType();
if (type != null) {
return type;
}
handleUnresolvedType(t, true);
return null;
}
@Override
public boolean setValidator(Predicate<JSType> validator) {
// If the type is already resolved, we can validate it now. If
// the type has not been resolved yet, we need to wait till its
// resolved before we can validate it.
if (this.isResolved()) {
return super.setValidator(validator);
} else {
this.validator = validator;
return true;
}
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
/**
* Bottom type, representing the subclass of any value or object.
*
* Although JavaScript programmers can't explicitly denote the bottom type,
* it comes up in static analysis. For example, if we have:
* <code>
* var x = null;
* if (x) {
* f(x);
* }
* </code>
* We need to be able to assign {@code x} a type within the {@code f(x)}
* call. Since it has no possible type, we assign {@code x} the NoType,
* so that {@code f(x)} is legal no matter what the type of {@code f}'s
* first argument is.
*
* @see <a href="http://en.wikipedia.org/wiki/Bottom_type">Bottom types</a>
*/
public class NoType extends NoObjectType {
private static final long serialVersionUID = 1L;
NoType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isNoObjectType() {
return false;
}
@Override
public boolean isNoType() {
return true;
}
@Override
public boolean isNullable() {
return true;
}
@Override
public boolean isSubtype(JSType that) {
return true;
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.EMPTY;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseNoType
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.TRUE;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.Node;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* An enum type representing a branded collection of elements. Each element
* is referenced by its name, and has an {@link EnumElementType} type.
*/
public class EnumType extends PrototypeObjectType {
private static final long serialVersionUID = 1L;
// the type of the individual elements
private EnumElementType elementsType;
// the elements' names (they all have the same type)
private final Set<String> elements = new HashSet<String>();
/**
* Creates an enum type.
*
* @param name the enum's name
* @param elementsType the base type of the individual elements
*/
EnumType(JSTypeRegistry registry, String name, JSType elementsType) {
super(registry, "enum{" + name + "}", null);
this.elementsType = new EnumElementType(registry, elementsType, name);
}
@Override
public boolean isEnumType() {
return true;
}
@Override
public ObjectType getImplicitPrototype() {
return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE);
}
/**
* Gets the elements defined on this enum.
* @return the elements' names defined on this enum. The returned set is
* immutable.
*/
public Set<String> getElements() {
return Collections.unmodifiableSet(elements);
}
/**
* Defines a new element on this enum.
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
* @param name the name of the new element
* @param definingNode the {@code Node} that defines this new element
* @return true iff the new element is added successfully
*/
public boolean defineElement(String name, Node definingNode) {
elements.add(name);
return defineDeclaredProperty(name, elementsType, false, definingNode);
}
/**
* Gets the elements' type.
*/
public EnumElementType getElementsType() {
return elementsType;
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
return this.isEquivalentTo(that) ? TRUE : FALSE;
}
@Override
public boolean isSubtype(JSType that) {
return that.isEquivalentTo(getNativeType(JSTypeNative.OBJECT_TYPE)) ||
that.isEquivalentTo(getNativeType(JSTypeNative.OBJECT_PROTOTYPE)) ||
JSType.isSubtype(this, that);
}
@Override
public String toString() {
return getReferenceName();
}
@Override
public String getDisplayName() {
return elementsType.getDisplayName();
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseObjectType(this);
}
@Override
public FunctionType getConstructor() {
return null;
}
@Override
public boolean matchesNumberContext() {
return false;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return true;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
elementsType = (EnumElementType) elementsType.resolve(t, scope);
return super.resolveInternal(t, scope);
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
/**
* An object type with a declared default element type, such as
* <code>Array.<string></code>.
*
* // TODO(user): Define the subtyping relation for parameterized types. Also,
* take parameterized type into account for equality.
*
*/
final class ParameterizedType extends ProxyObjectType {
private static final long serialVersionUID = 1L;
final JSType parameterType;
ParameterizedType(
JSTypeRegistry registry, ObjectType objectType, JSType parameterType) {
super(registry, objectType);
this.parameterType = parameterType;
}
@Override
public JSType getParameterType() {
return parameterType;
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.common.collect.ImmutableSet;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import java.util.Collections;
import java.util.Set;
/**
* An object type which uses composition to delegate all calls.
*
* @see NamedType
* @see ParameterizedType
*
*/
class ProxyObjectType extends ObjectType {
private static final long serialVersionUID = 1L;
private JSType referencedType;
private ObjectType referencedObjType;
ProxyObjectType(JSTypeRegistry registry, JSType referencedType) {
super(registry);
setReferencedType(referencedType);
}
JSType getReferencedTypeInternal() {
return referencedType;
}
void setReferencedType(JSType referencedType) {
this.referencedType = referencedType;
if (referencedType instanceof ObjectType) {
this.referencedObjType = (ObjectType) referencedType;
} else {
this.referencedObjType = null;
}
}
@Override
public String getReferenceName() {
return referencedObjType == null ?
"" : referencedObjType.getReferenceName();
}
@Override
public boolean hasReferenceName() {
return referencedObjType == null ?
null : referencedObjType.hasReferenceName();
}
@Override public boolean matchesNumberContext() {
return referencedType.matchesNumberContext();
}
@Override
public boolean matchesStringContext() {
return referencedType.matchesStringContext();
}
@Override public boolean matchesObjectContext() {
return referencedType.matchesObjectContext();
}
@Override
public boolean canBeCalled() {
return referencedType.canBeCalled();
}
@Override
public boolean isNoType()
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>Type findPropertyType(String propertyName) {
return referencedType.findPropertyType(propertyName);
}
@Override
public JSType getPropertyType(String propertyName) {
return referencedObjType == null ?
getNativeType(JSTypeNative.UNKNOWN_TYPE) :
referencedObjType.getPropertyType(propertyName);
}
@Override
public JSDocInfo getJSDocInfo() {
return referencedType.getJSDocInfo();
}
@Override
public void setJSDocInfo(JSDocInfo info) {
if (referencedObjType != null) {
referencedObjType.setJSDocInfo(info);
}
}
@Override
public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) {
return referencedObjType == null ? null :
referencedObjType.getOwnPropertyJSDocInfo(propertyName);
}
@Override
public void setPropertyJSDocInfo(String propertyName, JSDocInfo info,
boolean inExterns) {
if (referencedObjType != null) {
referencedObjType.setPropertyJSDocInfo(propertyName, info, inExterns);
}
}
@Override
public boolean hasProperty(String propertyName) {
return referencedObjType == null ? false :
referencedObjType.hasProperty(propertyName);
}
@Override
public boolean hasOwnProperty(String propertyName) {
return referencedObjType == null ? false :
referencedObjType.hasOwnProperty(propertyName);
}
@Override
public Set<String> getOwnPropertyNames() {
return referencedObjType == null ? ImmutableSet.<String>of() :
referencedObjType.getOwnPropertyNames();
}
@Override
public FunctionType getConstructor() {
return referencedObjType == null ? null :
referencedObjType.getConstructor();
}
@Override
public JSType getParameterType() {
return referencedObjType == null ? null :
referencedObjType.getParameterType();
}
@Override
public JSType getIndexType() {
return referencedObjType == null ? null :
referencedObjType.getIndexType();
}
@Override
public <T> T visit(Visitor<T> visitor) {
return referencedType.visit(visitor);
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
setReferencedType(referencedType.resolve(t, scope));
return this;
}
@Override
public String toDebugHashCodeString() {
return "{proxy:" + referencedType.toDebugHashCodeString() + "}";
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* A builder for the Rhino Node representing Function parameters.
* @author nicksantos@google.com (Nick Santos)
*/
public class FunctionParamBuilder {
private final JSTypeRegistry registry;
private final Node root = new Node(Token.LP);
public FunctionParamBuilder(JSTypeRegistry registry) {
this.registry = registry;
}
/**
* Add parameters of the given type to the end of the param list.
* @return False if this is called after optional params are added.
*/
public boolean addRequiredParams(JSType ...types) {
if (hasOptionalOrVarArgs()) {
return false;
}
for (JSType type : types) {
newParameter(type);
}
return true;
}
/**
* Add optional parameters of the given type to the end of the param list.
* @param types Types for each optional parameter. The builder will make them
* undefineable.
* @return False if this is called after var args are added.
*/
public boolean addOptionalParams(JSType ...types) {
if (hasVarArgs()) {
return false;
}
for (JSType type : types) {
newParameter(registry.createOptionalType(type)).setOptionalArg(true);
}
return true;
}
/**
* Add variable arguments to the end of the parameter list.
* @return False if this is called after var args are added.
*/
public boolean addVarArgs(JSType type) {
if (hasVarArgs()) {
return false;
}
// There are two types of variable argument functions:
// 1) Programmer
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>-defined var args
// 2) Native bottom types that can accept any argument.
// For the first one, "undefined" is a valid value for all arguments.
// For the second, we do not want to cast it up to undefined.
if (!type.isEmptyType()) {
type = registry.createOptionalType(type);
}
newParameter(type).setVarArgs(true);
return true;
}
/**
* Copies the parameter specification from the given node.
*/
public Node newParameterFromNode(Node n) {
Node newParam = newParameter(n.getJSType());
newParam.setVarArgs(n.isVarArgs());
newParam.setOptionalArg(n.isOptionalArg());
return newParam;
}
// Add a parameter to the list with the given type.
private Node newParameter(JSType type) {
Node paramNode = Node.newString(Token.NAME, "");
paramNode.setJSType(type);
root.addChildToBack(paramNode);
return paramNode;
}
public Node build() {
return root;
}
private boolean hasOptionalOrVarArgs() {
Node lastChild = root.getLastChild();
return lastChild != null &&
(lastChild.isOptionalArg() || lastChild.isVarArgs());
}
public boolean hasVarArgs() {
Node lastChild = root.getLastChild();
return lastChild != null && lastChild.isVarArgs();
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>,
ObjectType delegator, FunctionType delegateProxy,
FunctionType findDelegate);
/**
* @return the name of the delegate superclass.
*/
public String getDelegateSuperclassName();
/**
* Defines the delegate proxy prototype properties. Their types depend on
* properties of the delegate base methods.
*
* @param delegateProxyPrototypes List of delegate proxy prototypes.
*/
public void defineDelegateProxyPrototypeProperties(
JSTypeRegistry registry, Scope scope,
List<ObjectType> delegateProxyPrototypes);
/**
* Gets the name of the global object.
*/
public String getGlobalObject();
/**
* Whether this CALL function is testing for the existence of a property.
*/
public boolean isPropertyTestFunction(Node call);
/**
* Checks if the given method performs a object literal cast, and if it does,
* returns information on the cast. By default, always returns null. Meant
* to be overridden by subclasses.
*
* @param t The node traversal.
* @param callNode A CALL node.
*/
public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t,
Node callNode);
/**
* Returns the set of AssertionFunction.
*/
public Collection<AssertionFunctionSpec> getAssertionFunctions();
static enum SubclassType {
INHERITS,
MIXIN
}
static class SubclassRelationship {
final SubclassType type;
final String subclassName;
final String superclassName;
SubclassRelationship(SubclassType type,
Node subclassNode, Node superclassNode) {
this.type = type;
this.subclassName = subclassNode.getQualifiedName();
this.superclassName = superclassNode.getQualifiedName();
}
}
/**
* Delegates provides a mechanism and structure for identifying where classes
* can call out to optional code to augment their functionality. The optional
* code is isolated from the base code through the use of a subclass in the
* optional code derived from the delegate class in the base code.
*/
static class DelegateRelationship {
/** The subclass in the base code. */
final String delegateBase;
/** The class in the base code. */
final String delegator;
DelegateRelationship(String delegateBase, String delegator) {
this.delegateBase = delegateBase;
this.delegator = delegator;
}
}
/**
* An object literal cast provides a mechanism to cast object literals to
* other types without a warning.
*/
static class ObjectLiteralCast {
/** Type to cast to. */
final String typeName;
/** Object to cast. */
final Node objectNode;
ObjectLiteralCast(String typeName, Node objectNode) {
this.typeName = typeName;
this.objectNode = objectNode;
}
}
/**
* A function that will throw an exception when either:
* -One or more of its parameters evaluate to false.
* -One or more of its parameters are not of a certain type.
*/
public class AssertionFunctionSpec {
private final String functionName;
private final JSTypeNative assertedType;
public AssertionFunctionSpec(String functionName) {
this(functionName, null);
}
public AssertionFunctionSpec(String functionName,
JSTypeNative assertedType) {
this.functionName = functionName;
this.assertedType = assertedType;
}
/** Returns the name of the function. */
public String getFunctionName() {
return functionName;
}
/**
* Returns the parameter of the assertion function that is being checked.
* @param firstParam The first parameter of the function call.
*/
public Node getAssertedParam(Node firstParam) {
return firstParam;
}
/**
* Returns
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> the type for a type assertion, or null if the function asserts
* that the node must not be null or undefined.
*/
public JSTypeNative getAssertedType() {
return assertedType;
}
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.javascript.rhino.ErrorReporter;
import java.util.Collection;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* The {@code UnionType} implements a common JavaScript idiom in which the
* code is specifically designed to work with multiple input types. Because
* JavaScript always knows the runtime type of an object value, this is safer
* than a C union.<p>
*
* For instance, values of the union type {@code (String,boolean)} can be of
* type {@code String} or of type {@code boolean}. The commutativity of the
* statement is captured by making {@code (String,boolean)} and
* {@code (boolean,String)} equal.<p>
*
* The implementation of this class prevents the creation of nested
* unions.<p>
*/
public class UnionType extends JSType {
private static final long serialVersionUID = 1L;
Collection<JSType> alternates;
private final int hashcode;
/**
* Creates a union type.
*
* @param alternates the alternates of the union
*/
UnionType(JSTypeRegistry registry, Collection<JSType> alternates) {
super(registry);
this.alternates = alternates;
this.hashcode = this.alternates.hashCode();
}
/**
* Gets
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
}
@Override
public boolean canBeCalled() {
for (JSType t : alternates) {
if (!t.canBeCalled()) {
return false;
}
}
return true;
}
@Override
public JSType restrictByNotNullOrUndefined() {
UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
for (JSType t : alternates) {
restricted.addAlternate(t.restrictByNotNullOrUndefined());
}
return restricted.build();
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = null;
for (JSType t : alternates) {
TernaryValue test = t.testForEquality(that);
if (result == null) {
result = test;
} else if (!result.equals(test)) {
return UNKNOWN;
}
}
return result;
}
/**
* This predicate determines whether objects of this type can have the
* {@code null} value, and therefore can appear in contexts where
* {@code null} is expected.
*
* @return {@code true} for everything but {@code Number} and
* {@code Boolean} types.
*/
@Override
public boolean isNullable() {
for (JSType t : alternates) {
if (t.isNullable()) {
return true;
}
}
return false;
}
@Override
public boolean isUnknownType() {
for (JSType t : alternates) {
if (t.isUnknownType()) {
return true;
}
}
return false;
}
@Override
public JSType getLeastSupertype(JSType that) {
if (!that.isUnknownType() && !that.isUnionType()) {
for (JSType alternate : alternates) {
if (!alternate.isUnknownType() && that.isSubtype(alternate)) {
return this;
}
}
}
return getLeastSupertype(this, that);
}
JSType meet(JSType that) {
UnionTypeBuilder builder = new UnionTypeBuilder(registry);
for (JSType alternate : alternates) {
if (alternate.isSubtype(that)) {
builder.addAlternate(alternate);
}
}
if (that instanceof UnionType) {
for (JSType otherAlternate : ((UnionType) that).alternates) {
if (otherAlternate.isSubtype(this)) {
builder.addAlternate(otherAlternate);
}
}
} else if (that.isSubtype(this)) {
builder.addAlternate(that);
}
JSType result = builder.build();
if (!result.isNoType()) {
return result;
} else if (this.isObject() && that.isObject()) {
return getNativeType(JSTypeNative.NO_OBJECT_TYPE);
} else {
return getNativeType(JSTypeNative.NO_TYPE);
}
}
/**
* Two union types are equal if they have the same number of alternates
* and all alternates are equal.
*/
@Override
public boolean isEquivalentTo(JSType object) {
if (object instanceof UnionType) {
UnionType that = (UnionType) object;
if (alternates.size() != that.alternates.size()) {
return false;
}
for (JSType alternate : that.alternates) {
if (!hasAlternate(alternate)) {
return false;
}
}
return true;
} else {
return false;
}
}
private boolean hasAlternate(JSType type) {
for (JSType alternate : alternates) {
if (alternate.isEquivalent
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>To(type)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return this.hashcode;
}
@Override
public boolean isUnionType() {
return true;
}
@Override
public boolean isObject() {
for (JSType alternate : alternates) {
if (!alternate.isObject()) {
return false;
}
}
return true;
}
/**
* A {@link UnionType} contains a given type (alternate) iff the member
* vector contains it.
*
* @param alternate The alternate which might be in this union.
*
* @return {@code true} if the alternate is in the union
*/
public boolean contains(JSType type) {
for (JSType alt : alternates) {
if (alt.isEquivalentTo(type)) {
return true;
}
}
return false;
}
/**
* Returns a more restricted union type than {@code this} one, in which all
* subtypes of {@code type} have been removed.<p>
*
* Examples:
* <ul>
* <li>{@code (number,string)} restricted by {@code number} is
* {@code string}</li>
* <li>{@code (null, EvalError, URIError)} restricted by
* {@code Error} is {@code null}</li>
* </ul>
*
* @param type the supertype of the types to remove from this union type
*/
public JSType getRestrictedUnion(JSType type) {
UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
for (JSType t : alternates) {
if (t.isUnknownType() || !t.isSubtype(type)) {
restricted.addAlternate(t);
}
}
return restricted.build();
}
@Override public String toString() {
StringBuilder result = new StringBuilder();
boolean firstAlternate = true;
result.append("(");
SortedSet<JSType> sorted = new TreeSet<JSType>(ALPHA);
sorted.addAll(alternates);
for (JSType t : sorted) {
if (!firstAlternate) {
result.append("|");
}
result.append(t.toString());
firstAlternate = false;
}
result.append(")");
return result.toString();
}
@Override
public boolean isSubtype(JSType that) {
// unknown
if (that.isUnknownType()) {
return true;
}
// all type
if (that.isAllType()) {
return true;
}
for (JSType element : alternates) {
if (!element.isSubtype(that)) {
return false;
}
}
return true;
}
@Override
public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) {
// gather elements after restriction
UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
for (JSType element : alternates) {
restricted.addAlternate(
element.getRestrictedTypeGivenToBooleanOutcome(outcome));
}
return restricted.build();
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
BooleanLiteralSet literals = BooleanLiteralSet.EMPTY;
for (JSType element : alternates) {
literals = literals.union(element.getPossibleToBooleanOutcomes());
if (literals == BooleanLiteralSet.BOTH) {
break;
}
}
return literals;
}
@Override
public TypePair getTypesUnderEquality(JSType that) {
UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry);
UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry);
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
for (JSType element : alternates) {
TypePair p = element.getTypesUnderEquality(that);
if (p.typeA != null) {
thisRestricted.addAlternate(p.typeA);
}
if (p.typeB != null) {
thatRestricted.addAlternate(p.typeB);
}
}
return new TypePair(
thisRestricted.build(),
thatRestricted.build());
}
@Override
public TypePair getTypesUnderInequality(JSType that) {
UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry);
UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry);
for (JSType element : alternates) {
TypePair p = element.getTypesUnderInequality(that);
if (p.typeA != null) {
thisRestricted.addAlternate(p.typeA);
}
if (p.typeB != null) {
thatRestricted.addAlternate(p.typeB);
}
}
return new TypePair(
thisRestricted.build(),
thatRestricted.build());
}
@Override
public TypePair getTypesUnderShallowInequality(JSType that) {
UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry);
UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry);
for (JSType element : alternates) {
TypePair p = element.getTypesUnderShallowInequality(that);
if (p.typeA != null) {
thisRestricted.addAlternate(p.typeA);
}
if (p.typeB != null) {
thatRestricted.addAlternate(p.typeB);
}
}
return new TypePair(
thisRestricted.build(),
thatRestricted.build());
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseUnionType(this);
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
setResolvedTypeInternal(this); // for circularly defined types.
boolean changed = false;
ImmutableList.Builder<JSType> resolvedTypes = ImmutableList.builder();
for (JSType alternate : alternates) {
JSType newAlternate = alternate.resolve(t, scope);
changed |= (alternate != newAlternate);
resolvedTypes.add(alternate);
}
if (changed) {
Collection<JSType> newAlternates = resolvedTypes.build();
Preconditions.checkState(
newAlternates.hashCode() == this.hashcode);
alternates = newAlternates;
}
return this;
}
@Override
public String toDebugHashCodeString() {
List<String> hashCodes = Lists.newArrayList();
for (JSType a : alternates) {
hashCodes.add(a.toDebugHashCodeString());
}
return "{(" + Joiner.on(",").join(hashCodes) + ")}";
}
@Override
public boolean setValidator(Predicate<JSType> validator) {
for (JSType a : alternates) {
a.setValidator(validator);
}
return true;
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
/**
* The bottom Object type, representing the subclass of all objects.
*
* Although JavaScript programmers can't explicitly denote the bottom
* Object type, it comes up in static analysis. For example, if we have:
* <code>
* var x = function() {};
* if (x instanceof Array) {
* f(x);
* }
* </code>
* We need to be able to assign {@code x} a type within the {@code f(x)}
* call. It has no possible type, but {@code x} would not be legal if f
* expected a string. So we assign it the {@code NoObjectType}.
*
* @see <a href="http://en.wikipedia.org/wiki/Bottom_type">Bottom types</a>
*/
public class NoObjectType extends FunctionType {
private static final long serialVersionUID = 1L;
NoObjectType(JSTypeRegistry registry) {
super(registry, null, null,
registry.createArrowType(null, null),
null, null, true, true);
getInternalArrowType().returnType = this;
this.setInstanceType(this);
}
@Override
public boolean isSubtype(JSType that) {
if (JSType.isSubtype(this, that)) {
return true;
} else {
return that.isObject() && !that.isNoType() && !that.isNoResolvedType();
}
}
@Override
public boolean isFunctionType() {
return false;
}
@Override
public boolean isNoObjectType() {
return true;
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> }
@Override
public ObjectType getImplicitPrototype() {
return null;
}
@Override
public String getReferenceName() {
return null;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean isEquivalentTo(JSType that) {
return this == that;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public int getPropertiesCount() {
// Should never be called, returning the biggest number to highlight the
// 'unifying' role of this type.
return Integer.MAX_VALUE;
}
@Override
public JSType getPropertyType(String propertyName) {
// Return the least type to be a proper subtype of all other objects.
return getNativeType(JSTypeNative.NO_TYPE);
}
@Override
public boolean hasProperty(String propertyName) {
// has all properties, since it is any object
return true;
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, boolean inExterns, Node propertyNode) {
// nothing, all properties are defined
return true;
}
@Override
public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) {
return null;
}
@Override
public void setPropertyJSDocInfo(String propertyName, JSDocInfo info,
boolean inExterns) {
// Do nothing, specific properties do not have JSDocInfo.
}
@Override
public boolean isPropertyTypeInferred(String propertyName) {
return false;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseNoObjectType();
}
@Override
public String toString() {
return "NoObject";
}
@Override
public FunctionType getConstructor() {
return null;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
return this;
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> public void applySingletonGetter(FunctionType functionType,
FunctionType getterType, ObjectType objectType) {
// do nothing.
}
@Override
public DelegateRelationship getDelegateRelationship(Node callNode) {
return null;
}
@Override
public void applyDelegateRelationship(
ObjectType delegateSuperclass, ObjectType delegateBase,
ObjectType delegator, FunctionType delegateProxy,
FunctionType findDelegate) {
// do nothing.
}
@Override
public String getDelegateSuperclassName() {
return null;
}
@Override
public void defineDelegateProxyPrototypeProperties(
JSTypeRegistry registry, Scope scope,
List<ObjectType> delegateProxyPrototypes) {
// do nothing.
}
@Override
public String getGlobalObject() {
return "window";
}
@Override
public boolean isPropertyTestFunction(Node call) {
return false;
}
@Override
public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t,
Node callNode) {
return null;
}
@Override
public Collection<AssertionFunctionSpec> getAssertionFunctions() {
return Collections.emptySet();
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
/**
* Constants corresponding to types that are built into a JavaScript engine
* and other types that occur very often in the type system. See
* {@link com.google.javascript.rhino.jstype.JSTypeRegistry#getNativeType(JSTypeNative)}.
*/
public enum JSTypeNative {
// Built-in types (please keep alphabetized)
ARRAY_TYPE,
ARRAY_FUNCTION_TYPE,
BOOLEAN_TYPE,
BOOLEAN_OBJECT_TYPE,
BOOLEAN_OBJECT_FUNCTION_TYPE,
/**
* A checked unknown type is a type that we know something about,
* but we're not really sure what we know about it.
*
* Examples of checked unknown types include:
* <code>
* if (x) { // x is unknown
* alert(x); // x is checked unknown
* }
* </code>
*
* <code>
* /* @param {SomeForwardDeclaredType} x /
* function f(x) {
* // x is checked unknown. We know it's some type, but the type
* // has not been included in this binary.
* }
* </code>
*
* This is useful for missing property warnings, where we don't
* want to emit warnings on things that have been checked.
*/
CHECKED_UNKNOWN_TYPE,
DATE_TYPE,
DATE_FUNCTION_TYPE,
ERROR_FUNCTION_TYPE,
ERROR_TYPE,
EVAL_ERROR_FUNCTION_TYPE,
EVAL_ERROR_TYPE,
FUNCTION_FUNCTION_TYPE,
FUNCTION_INSTANCE_TYPE, // equivalent to U2U_CONSTRUCTOR_TYPE
FUNCTION_PROTOTYPE,
NULL_TYPE,
NUMBER_TYPE,
NUMBER_OBJECT_TYPE,
NUMBER_OBJECT_FUNCTION_TYPE,
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
OBJECT_TYPE,
OBJECT_FUNCTION_TYPE,
OBJECT_PROTOTYPE,
RANGE_ERROR_FUNCTION_TYPE,
RANGE_ERROR_TYPE,
REFERENCE_ERROR_FUNCTION_TYPE,
REFERENCE_ERROR_TYPE,
REGEXP_TYPE,
REGEXP_FUNCTION_TYPE,
STRING_OBJECT_TYPE,
STRING_OBJECT_FUNCTION_TYPE,
STRING_TYPE,
SYNTAX_ERROR_FUNCTION_TYPE,
SYNTAX_ERROR_TYPE,
TYPE_ERROR_FUNCTION_TYPE,
TYPE_ERROR_TYPE,
UNKNOWN_TYPE,
URI_ERROR_FUNCTION_TYPE,
URI_ERROR_TYPE,
VOID_TYPE,
// Commonly used types
TOP_LEVEL_PROTOTYPE,
STRING_VALUE_OR_OBJECT_TYPE,
NUMBER_VALUE_OR_OBJECT_TYPE,
ALL_TYPE,
NO_TYPE,
NO_OBJECT_TYPE,
NO_RESOLVED_TYPE,
GLOBAL_THIS,
U2U_CONSTRUCTOR_TYPE,
U2U_FUNCTION_TYPE,
LEAST_FUNCTION_TYPE,
GREATEST_FUNCTION_TYPE,
/**
* (Object,number,string)
*/
OBJECT_NUMBER_STRING,
/**
* (Object,number,string,boolean)
*/
OBJECT_NUMBER_STRING_BOOLEAN,
/**
* (number,string,boolean)
*/
NUMBER_STRING_BOOLEAN,
/**
* (number,string)
*/
NUMBER_STRING,
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.common.collect.Maps;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.RecordTypeBuilder.RecordProperty;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
/**
* A record (structural) type.
*
* Subtyping: The subtyping of a record type is defined via structural
* comparison of a record type's properties. For example, a record
* type of the form { a : TYPE_1 } is a supertype of a record type
* of the form { b : TYPE_2, a : TYPE_1 } because B can be assigned to
* A and matches all constraints. Similarly, a defined type can be assigned
* to a record type so long as that defined type matches all property
* constraints of the record type. A record type of the form { a : A, b : B }
* can be assigned to a record of type { a : A }.
*
*/
public class RecordType extends PrototypeObjectType {
private static final long serialVersionUID = 1L;
private final SortedMap<String, JSType> properties = Maps.newTreeMap();
private boolean isFrozen = false;
/**
* Creates a record type.
*
* @param registry The type registry under which this type lives.
* @param properties A map of all the properties of this record type.
* @throws IllegalStateException if the {@code RecordProperty} associated
* with a property is null.
*/
RecordType(JSTypeRegistry registry, Map<String, RecordProperty> properties) {
super(registry, null, null);
for (String property : properties
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>.keySet()) {
RecordProperty prop = properties.get(property);
if (prop == null) {
throw new IllegalStateException(
"RecordProperty associated with a property should not be null!");
}
defineDeclaredProperty(property, prop.getType(), false, prop.getPropertyNode());
}
// Freeze the record type.
isFrozen = true;
}
@Override
public boolean isEquivalentTo(JSType other) {
if (!(other instanceof RecordType)) {
return false;
}
// Compare properties.
RecordType otherRecord = (RecordType) other;
Set<String> keySet = properties.keySet();
Map<String, JSType> otherProps = otherRecord.properties;
if (!otherProps.keySet().equals(keySet)) {
return false;
}
for (String key : keySet) {
if (!otherProps.get(key).isEquivalentTo(properties.get(key))) {
return false;
}
}
return true;
}
@Override
public ObjectType getImplicitPrototype() {
return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE);
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, boolean inExterns, Node propertyNode) {
if (isFrozen) {
return false;
}
if (!inferred) {
properties.put(propertyName, type);
}
return super.defineProperty(propertyName, type, inferred, inExterns,
propertyNode);
}
@Override
public JSType getLeastSupertype(JSType that) {
if (!that.isRecordType()) {
return super.getLeastSupertype(that);
}
RecordType thatRecord = (RecordType) that;
RecordTypeBuilder builder = new RecordTypeBuilder(registry);
// The least supertype consist of those properties of the record
// type that both record types hold in common both by name and
// type of the properties themselves.
for (String property : properties.keySet()) {
if (thatRecord.hasProperty(property) &&
thatRecord.getPropertyType(property).isEquivalentTo(
getPropertyType(property))) {
builder.addProperty(property, getPropertyType(property),
getPropertyNode(property));
}
}
return builder.build();
}
@Override
public JSType getGreatestSubtype(JSType that) {
if (that.isRecordType()) {
RecordType thatRecord = (RecordType) that;
RecordTypeBuilder builder = new RecordTypeBuilder(registry);
// The greatest subtype consists of those *unique* properties of both
// record types. If any property conflicts, then the NO_TYPE type
// is returned.
for (String property : properties.keySet()) {
if (thatRecord.hasProperty(property) &&
!thatRecord.getPropertyType(property).isEquivalentTo(
getPropertyType(property))) {
return registry.getNativeObjectType(JSTypeNative.NO_TYPE);
}
builder.addProperty(property, getPropertyType(property),
getPropertyNode(property));
}
for (String property : thatRecord.properties.keySet()) {
if (!hasProperty(property)) {
builder.addProperty(property, thatRecord.getPropertyType(property),
thatRecord.getPropertyNode(property));
}
}
return builder.build();
}
JSType greatestSubtype = super.getGreatestSubtype(that);
if (greatestSubtype.isNoObjectType() && !that.isNoObjectType()) {
// In this branch, the other type is some object type. We find
// the greatest subtype with the following algorithm:
// 1) For each property "x" of this record type, take the union
//
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> of all classes with a property "x" with a compatible property type.
// and which are a subtype of {@code that}.
// 2) Take the intersection of all of these unions.
for (Map.Entry<String, JSType> entry : properties.entrySet()) {
String propName = entry.getKey();
JSType propType = entry.getValue();
UnionTypeBuilder builder = new UnionTypeBuilder(registry);
for (ObjectType alt :
registry.getEachReferenceTypeWithProperty(propName)) {
JSType altPropType = alt.getPropertyType(propName);
if (altPropType != null && !alt.isEquivalentTo(this) &&
alt.isSubtype(that) &&
(propType.isUnknownType() || altPropType.isUnknownType() ||
altPropType.isEquivalentTo(propType))) {
builder.addAlternate(alt);
}
}
greatestSubtype = greatestSubtype.getLeastSupertype(builder.build());
}
}
return greatestSubtype;
}
@Override
public boolean isRecordType() {
return true;
}
@Override
public boolean isSubtype(JSType that) {
if (JSType.isSubtype(this, that)) {
return true;
}
// Top of the record types is the empty record, or OBJECT_TYPE.
if (registry.getNativeObjectType(
JSTypeNative.OBJECT_TYPE).isSubtype(that)) {
return true;
}
// A type is a subtype of a record type if it itself is a record
// type and it has at least the same members as the parent record type
// with the same types.
if (!that.isRecordType()) {
return false;
}
return RecordType.isSubtype(this, (RecordType) that);
}
/** Determines if typeA is a subtype of typeB */
static boolean isSubtype(ObjectType typeA, RecordType typeB) {
// typeA is a subtype of record type typeB iff:
// 1) typeA has all the properties declared in typeB.
// 2) And for each property of typeB,
// 2a) if the property of typeA is declared, it must be equal
// to the type of the property of typeB,
// 2b) otherwise, it must be a subtype of the property of typeB.
//
// To figure out why this is true, consider the following pseudo-code:
// /** @type {{a: (Object,null)}} */ var x;
// /** @type {{a: !Object}} */ var y;
// var z = {a: {}};
// x.a = null;
//
// y cannot be assigned to x, because line 4 would violate y's declared
// properties. But z can be assigned to x. Even though z and y are the
// same type, the properties of z are inferred--and so an assignment
// to the property of z would not violate any restrictions on it.
for (String property : typeB.properties.keySet()) {
if (!typeA.hasProperty(property)) {
return false;
}
JSType propA = typeA.getPropertyType(property);
JSType propB = typeB.getPropertyType(property);
if (!propA.isUnknownType() && !propB.isUnknownType()) {
if (typeA.isPropertyTypeDeclared(property)) {
if (!propA.isEquivalentTo(propB)) {
return false;
}
} else {
if (!propA.isSubtype(propB)) {
return false;
}
}
}
}
return true;
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.JSTypeNative.ALL_TYPE;
/**
* This type is for built-in error constructors.
*/
class ErrorFunctionType extends FunctionType {
private static final long serialVersionUID = 1L;
ErrorFunctionType(JSTypeRegistry registry, String name) {
super(
registry, name, null,
registry.createArrowType(
registry.createOptionalParameters(
registry.getNativeType(ALL_TYPE),
registry.getNativeType(ALL_TYPE),
registry.getNativeType(ALL_TYPE)),
null),
null, null, true, true);
// NOTE(nicksantos): Errors have the weird behavior in that they can
// be called as functions, and they will return instances of themselves.
// Error('x') instanceof Error => true
//
// In user-defined types, we would deal with this case by creating
// a NamedType with the name "Error" and then resolve it later.
//
// For native types, we don't really want the native types to
// depend on type-resolution. So we just set the return type manually
// at the end of construction.
//
// There's similar logic in JSTypeRegistry for Array and RegExp.
getInternalArrowType().returnType = getInstanceType();
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
import com.google.javascript.rhino.ErrorReporter;
/**
* All type, representing all values.
*/
public final class AllType extends JSType {
private static final long serialVersionUID = 1L;
AllType(JSTypeRegistry registry) {
super(registry);
}
/**
* The All type is the greatest type (top) and is never a subtype of
* another except itself or the Unknown type or a named alias.
* @return {@code this.isEquivalentTo(that)}
*/
@Override
public boolean isSubtype(JSType that) {
return JSType.isSubtype(this, that);
}
@Override
public boolean isAllType() {
return true;
}
@Override
public boolean matchesStringContext() {
// Be lenient.
return true;
}
@Override
public boolean matchesObjectContext() {
// Be lenient.
return true;
}
@Override
public boolean canBeCalled() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
return UNKNOWN;
}
@Override
public String toString() {
return "*";
}
@Override
public String getDisplayName() {
return "<Any Type>";
}
@Override
public boolean hasDisplayName() {
return true;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseAllType();
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
return this;
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.JSTypeNative.ALL_TYPE;
import static com.google.javascript.rhino.jstype.JSTypeNative.CHECKED_UNKNOWN_TYPE;
import static com.google.javascript.rhino.jstype.JSTypeNative.NO_TYPE;
import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.javascript.rhino.jstype.UnionType;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
/**
* A builder for union types.
*
* @author nicksantos@google.com (Nick Santos)
*/
class UnionTypeBuilder implements Serializable {
private static final long serialVersionUID = 1L;
// If the best we can do is say "this object is one of twenty things",
// then we should just give up and admit that we have no clue.
private static final int MAX_UNION_SIZE = 20;
private final JSTypeRegistry registry;
private final List<JSType> alternates = Lists.newArrayList();
private boolean isAllType = false;
private boolean isNativeUnknownType = false;
private boolean areAllUnknownsChecked = true;
// Memoize the result, in case build() is called multiple times.
private JSType result = null;
UnionTypeBuilder(JSTypeRegistry registry) {
this.registry = registry;
}
Iterable<JSType> getAlternates() {
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> JSType specialCaseType = reduceAlternatesWithoutUnion();
if (specialCaseType != null) {
return ImmutableList.of(specialCaseType);
}
return alternates;
}
/**
* Adds an alternate to the union type under construction. Returns this
* for easy chaining.
*/
UnionTypeBuilder addAlternate(JSType alternate) {
// build() returns the bottom type by default, so we can
// just bail out early here.
if (alternate.isNoType()) {
return this;
}
isAllType = isAllType || alternate.isAllType();
boolean isAlternateUnknown = alternate instanceof UnknownType;
isNativeUnknownType = isNativeUnknownType || isAlternateUnknown;
if (isAlternateUnknown) {
areAllUnknownsChecked = areAllUnknownsChecked &&
alternate.isCheckedUnknownType();
}
if (!isAllType && !isNativeUnknownType) {
if (alternate instanceof UnionType) {
UnionType union = (UnionType) alternate;
for (JSType unionAlt : union.getAlternates()) {
addAlternate(unionAlt);
}
} else {
if (alternates.size() > MAX_UNION_SIZE) {
return this;
}
// Look through the alternates we've got so far,
// and check if any of them are duplicates of
// one another.
Iterator<JSType> it = alternates.iterator();
while (it.hasNext()) {
JSType current = it.next();
// Unknown and NoResolved types may just be names that haven't
// been resolved yet. So keep these in the union, and just use
// equality checking for simple de-duping.
if (alternate.isUnknownType() ||
current.isUnknownType() ||
alternate.isNoResolvedType() ||
current.isNoResolvedType()) {
if (alternate.isEquivalentTo(current)) {
// Alternate is unnecessary.
return this;
}
} else {
if (alternate.isSubtype(current)) {
// Alternate is unnecessary.
return this;
} else if (current.isSubtype(alternate)) {
// Alternate makes current obsolete
it.remove();
}
}
}
alternates.add(alternate);
result = null; // invalidate the memoized result
}
} else {
result = null;
}
return this;
}
/**
* Reduce the alternates into a non-union type.
* If the alternates can't be accurately represented with a non-union
* type, return null.
*/
private JSType reduceAlternatesWithoutUnion() {
if (isAllType) {
return registry.getNativeType(ALL_TYPE);
} else if (isNativeUnknownType) {
if (areAllUnknownsChecked) {
return registry.getNativeType(CHECKED_UNKNOWN_TYPE);
} else {
return registry.getNativeType(UNKNOWN_TYPE);
}
} else {
int size = alternates.size();
if (size > MAX_UNION_SIZE) {
return registry.getNativeType(UNKNOWN_TYPE);
} else if (size > 1) {
return null;
} else if (size == 1) {
return alternates.iterator().next();
} else {
return registry.getNativeType(NO_TYPE);
}
}
}
/**
* Creates a union.
* @return A UnionType if it has two or more alternates, the
* only alternate if it has one and otherwise {@code NO_TYPE}.
*/
JSType build() {
if (result == null) {
result = reduceAlternatesWithoutUnion();
if
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> (result == null) {
result = new UnionType(registry, getAlternateListCopy());
}
}
return result;
}
private static final Comparator<JSType> typeSorter =
new Comparator<JSType>() {
@Override public int compare(JSType a, JSType b) {
return b.hashCode() - a.hashCode();
}
};
private Collection<JSType> getAlternateListCopy() {
// TODO(nicksantos): Until we're at a place where we're no longer
// using java's built-in equals to test type equivalence, we need
// hash codes to be the same. So the alternates need to be sorted.
Collections.sort(alternates, typeSorter);
return ImmutableList.copyOf(alternates);
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.Node;
import java.util.Set;
/**
* The type of individual elements of an enum type
* (see {@link EnumType}).
*/
public class EnumElementType extends ObjectType {
private static final long serialVersionUID = 1L;
/**
* The primitive type this enum element type wraps. For instance, in
* the following code defining the {@code LOCAL_CODES} enum
* <pre>var LOCAL_CODES = {A: 3, B: 9, C: 8}</pre>
* the primitive type of the the constants is {@code number}.
*/
private JSType primitiveType;
// The primitive type, if it is an object.
private ObjectType primitiveObjectType;
private final String name;
EnumElementType(JSTypeRegistry registry, JSType elementType,
String name) {
super(registry);
this.primitiveType = elementType;
this.primitiveObjectType = elementType.toObjectType();
this.name = name;
}
@Override
public boolean isEnumElementType() {
return true;
}
@Override
public boolean matchesNumberContext() {
return primitiveType.matchesNumberContext();
}
@Override
public boolean matchesStringContext() {
return primitiveType.matchesStringContext();
}
@Override
public boolean matchesObjectContext() {
return primitiveType.matchesObjectContext();
}
@Override
public boolean canBeCalled() {
return primitiveType.canBeCalled();
}
@Override
public boolean isObject() {
return primitiveType.isObject();
}
@Override
public TernaryValue testForEquality(JSType that) {
return primitiveType.testForEquality(that);
}
/**
*
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> This predicate determines whether objects of this type can have the null
* value, and therefore can appear in contexts where null is expected.
*
* @return true for everything but Number and Boolean types.
*/
@Override public boolean isNullable() {
return primitiveType.isNullable();
}
@Override
public boolean isNominalType() {
return hasReferenceName();
}
@Override
public boolean isEquivalentTo(JSType that) {
if (this == that) {
return true;
} else if (this.isNominalType()) {
ObjectType thatObj = ObjectType.cast(that);
if (thatObj != null && thatObj.isNominalType()) {
return getReferenceName().equals(thatObj.getReferenceName());
}
}
return false;
}
/**
* If this is equal to a NamedType object, its hashCode must be equal
* to the hashCode of the NamedType object.
*/
@Override
public int hashCode() {
if (hasReferenceName()) {
return getReferenceName().hashCode();
} else {
return super.hashCode();
}
}
@Override
public String toString() {
return getReferenceName() + ".<" + primitiveType + ">";
}
@Override
public String getReferenceName() {
return name;
}
@Override
public boolean hasReferenceName() {
return true;
}
@Override
public boolean isSubtype(JSType that) {
if (JSType.isSubtype(this, that)) {
return true;
} else {
return primitiveType.isSubtype(that);
}
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseEnumElementType(this);
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, boolean inExterns, Node propertyNode) {
// nothing
return true;
}
@Override
public boolean isPropertyTypeDeclared(String propertyName) {
return primitiveObjectType == null ?
false : primitiveObjectType.isPropertyTypeDeclared(propertyName);
}
@Override
public boolean isPropertyTypeInferred(String propertyName) {
return primitiveObjectType == null ?
false : primitiveObjectType.isPropertyTypeInferred(propertyName);
}
@Override
public ObjectType getImplicitPrototype() {
return null;
}
@Override
public int getPropertiesCount() {
return primitiveObjectType == null ?
0 : primitiveObjectType.getPropertiesCount();
}
@Override
void collectPropertyNames(Set<String> props) {
if (primitiveObjectType != null) {
primitiveObjectType.collectPropertyNames(props);
}
}
@Override
public JSType findPropertyType(String propertyName) {
return primitiveType.findPropertyType(propertyName);
}
@Override
public JSType getPropertyType(String propertyName) {
return primitiveObjectType == null ?
getNativeType(JSTypeNative.UNKNOWN_TYPE) :
primitiveObjectType.getPropertyType(propertyName);
}
@Override
public boolean hasProperty(String propertyName) {
return primitiveObjectType == null ?
false :
primitiveObjectType.hasProperty(propertyName);
}
@Override
public FunctionType getConstructor() {
return primitiveObjectType == null ?
null : primitiveObjectType.getConstructor();
}
@Override
public JSType autoboxesTo() {
return primitiveType.autoboxesTo();
}
/**
* Gets the primitive type of this enum element.
*/
public JSType getPrimitiveType() {
return primitiveType;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
primitiveType = primitiveType.resolve(t, scope);
primitiveObjectType =
(ObjectType) safeResolve
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> We a) want to
* make sure that the type information specified in the externs file
* matches what is in the registry and b) annotate the externs with
* the {@link JSType} from the registry so that there are not two
* separate JSType objects for one type.
*/
private FunctionType getOrCreateConstructor() {
FunctionType fnType = typeRegistry.createConstructorType(
fnName, sourceNode, parametersNode, returnType);
JSType existingType = typeRegistry.getType(fnName);
if (existingType != null) {
boolean isInstanceObject = existingType instanceof InstanceObjectType;
if (isInstanceObject || fnName.equals("Function")) {
FunctionType existingFn =
isInstanceObject ?
((InstanceObjectType) existingType).getConstructor() :
typeRegistry.getNativeFunctionType(FUNCTION_FUNCTION_TYPE);
if (existingFn.getSource() == null) {
existingFn.setSource(sourceNode);
}
if (!existingFn.hasEqualCallType(fnType)) {
reportWarning(TYPE_REDEFINITION, fnName,
fnType.toString(), existingFn.toString());
}
return existingFn;
} else {
// We fall through and return the created type, even though it will fail
// to register. We have no choice as we have to return a function. We
// issue an error elsewhere though, so the user should fix it.
}
}
maybeSetBaseType(fnType);
if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) {
typeRegistry.declareType(fnName, fnType.getInstanceType());
}
return fnType;
}
private void reportWarning(DiagnosticType warning, String ... args) {
compiler.report(JSError.make(sourceName, errorRoot, warning, args));
}
private void reportError(DiagnosticType error, String ... args) {
compiler.report(JSError.make(sourceName, errorRoot, error, args));
}
/**
* Determines whether the given jsdoc info declares a function type.
*/
static boolean isFunctionTypeDeclaration(JSDocInfo info) {
return info.getParameterCount() > 0 ||
info.hasReturnType() ||
info.hasThisType() ||
info.isConstructor() ||
info.isInterface();
}
/**
* The scope that we should declare this function in, if it needs
* to be declared in a scope. Notice that TypedScopeCreator takes
* care of most scope-declaring.
*/
private Scope getScopeDeclaredIn() {
int dotIndex = fnName.indexOf(".");
if (dotIndex != -1) {
String rootVarName = fnName.substring(0, dotIndex);
Var rootVar = scope.getVar(rootVarName);
if (rootVar != null) {
return rootVar.getScope();
}
}
return scope;
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> = callName.getNext();
if (!typeNode.isQualifiedName()) {
return null;
}
Node objectNode = typeNode.getNext();
if (objectNode.getType() != Token.OBJECTLIT) {
t.getCompiler().report(JSError.make(t.getSourceName(), callNode,
OBJECTLIT_EXPECTED));
return null;
}
return new ObjectLiteralCast(typeNode.getQualifiedName(),
typeNode.getNext());
}
@Override
public boolean isOptionalParameter(Node parameter) {
return false;
}
@Override
public boolean isVarArgsParameter(Node parameter) {
return false;
}
@Override
public boolean isPrivate(String name) {
return false;
}
@Override
public Collection<AssertionFunctionSpec> getAssertionFunctions() {
return ImmutableList.<AssertionFunctionSpec>of(
new AssertionFunctionSpec("goog.asserts.assert"),
new AssertionFunctionSpec("goog.asserts.assertNumber",
JSTypeNative.NUMBER_TYPE),
new AssertionFunctionSpec("goog.asserts.assertString",
JSTypeNative.STRING_TYPE),
new AssertionFunctionSpec("goog.asserts.assertFunction",
JSTypeNative.FUNCTION_INSTANCE_TYPE),
new AssertionFunctionSpec("goog.asserts.assertObject",
JSTypeNative.OBJECT_TYPE),
new AssertionFunctionSpec("goog.asserts.assertArray",
JSTypeNative.ARRAY_TYPE),
// TODO(agrieve): It would be better if this could make the first
// parameter the type of the second parameter.
new AssertionFunctionSpec("goog.asserts.assertInstanceof",
JSTypeNative.OBJECT_TYPE)
);
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
/**
* Number type.
*/
public class NumberType extends ValueType {
private static final long serialVersionUID = 1L;
NumberType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isNullable() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isUnknownType() || that.isSubtype(
getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) {
return UNKNOWN;
}
return FALSE;
}
@Override
public boolean isNumberValueType() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
// TODO(user): Revisit this for ES4, which is stricter.
return true;
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "number";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseNumberType();
}
@Override
public JSType autoboxesTo() {
return getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE);
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.common.base.Preconditions;
import com.google.javascript.rhino.Node;
/**
* An object type that is an instance of some function constructor.
*/
public final class InstanceObjectType extends PrototypeObjectType {
private static final long serialVersionUID = 1L;
private final FunctionType constructor;
InstanceObjectType(JSTypeRegistry registry, FunctionType constructor) {
this(registry, constructor, false);
}
InstanceObjectType(JSTypeRegistry registry, FunctionType constructor,
boolean isNativeType) {
super(registry, null, null, isNativeType);
Preconditions.checkNotNull(constructor);
this.constructor = constructor;
}
@Override
public String getReferenceName() {
return getConstructor().getReferenceName();
}
@Override
public boolean hasReferenceName() {
return getConstructor().hasReferenceName();
}
@Override
public ObjectType getImplicitPrototype() {
return getConstructor().getPrototype();
}
@Override
public FunctionType getConstructor() {
return constructor;
}
@Override
boolean defineProperty(String name, JSType type, boolean inferred,
boolean inExterns, Node propertyNode) {
ObjectType proto = getImplicitPrototype();
if (proto != null && proto.hasOwnDeclaredProperty(name)) {
return false;
}
return super.defineProperty(name, type, inferred, inExterns, propertyNode);
}
@Override
public String toString() {
if (constructor.hasReferenceName()) {
return constructor.getReferenceName();
} else {
return super.toString();
}
}
@Override
boolean isTheObjectType() {
return getConstructor().isNative() && "Object".equals(getReferenceName());
}
@Override
public boolean isInstanceType() {
return true;
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> };",
"inconsistent return type\n" +
"found : (number|string)\n" +
"required: string");
}
public void testResolutionViaRegistry4() throws Exception {
testTypes("/** @constructor */ u.A = function() {};\n" +
"/**\n* @constructor\n* @extends {u.A}\n*/\nu.A.A = function() {}\n;" +
"/**\n* @constructor\n* @extends {u.A}\n*/\nu.A.B = function() {};\n" +
"var ab = new u.A.B();\n" +
"/** @type {!u.A} */ var a = ab;\n" +
"/** @type {!u.A.A} */ var aa = ab;\n",
"initializing variable\n" +
"found : u.A.B\n" +
"required: u.A.A");
}
public void testResolutionViaRegistry5() throws Exception {
Node n = parseAndTypeCheck("/** @constructor */ u.T = function() {}; u.T");
JSType type = n.getLastChild().getLastChild().getJSType();
assertFalse(type.isUnknownType());
assertTrue(type instanceof FunctionType);
assertEquals("u.T",
((FunctionType) type).getInstanceType().getReferenceName());
}
public void testGatherProperyWithoutAnnotation1() throws Exception {
Node n = parseAndTypeCheck("/** @constructor */ var T = function() {};" +
"/** @type {!T} */var t; t.x; t;");
JSType type = n.getLastChild().getLastChild().getJSType();
assertFalse(type.isUnknownType());
assertTrue(type instanceof ObjectType);
ObjectType objectType = (ObjectType) type;
assertFalse(objectType.hasProperty("x"));
assertEquals(
Lists.newArrayList(objectType),
registry.getTypesWithProperty("x"));
}
public void testGatherProperyWithoutAnnotation2() throws Exception {
TypeCheckResult ns =
parseAndTypeCheckWithScope("/** @type {!Object} */var t; t.x; t;");
Node n = ns.root;
Scope s = ns.scope;
JSType type = n.getLastChild().getLastChild().getJSType();
assertFalse(type.isUnknownType());
assertEquals(type, OBJECT_TYPE);
assertTrue(type instanceof ObjectType);
ObjectType objectType = (ObjectType) type;
assertFalse(objectType.hasProperty("x"));
assertEquals(
Lists.newArrayList(OBJECT_TYPE),
registry.getTypesWithProperty("x"));
}
public void testFunctionMasksVariableBug() throws Exception {
testTypes("var x = 4; var f = function x(b) { return b ? 1 : x(true); };",
"function x masks variable (IE bug)");
}
public void testDfa1() throws Exception {
testTypes("var x = null;\n x = 1;\n /** @type number */ var y = x;");
}
public void testDfa2() throws Exception {
testTypes("function u() {}\n" +
"/** @return {number} */ function f() {\nvar x = 'todo';\n" +
"if (u()) { x = 1; } else { x = 2; } return x;\n}");
}
public void testDfa3() throws Exception {
testTypes("function u() {}\n" +
"/** @return {number} */ function f() {\n" +
"/** @type {number|string} */ var x = 'todo';
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> parameter\n" +
"found : number\n" +
"required: string");
}
public void testLends1() throws Exception {
testTypes(
"function extend(x, y) {}" +
"/** @constructor */ function Foo() {}" +
"extend(Foo, /** @lends */ ({bar: 1}));",
"Parse error. missing object name in @lends tag");
}
public void testLends2() throws Exception {
testTypes(
"function extend(x, y) {}" +
"/** @constructor */ function Foo() {}" +
"extend(Foo, /** @lends {Foob} */ ({bar: 1}));",
"Variable Foob not declared before @lends annotation.");
}
public void testLends3() throws Exception {
testTypes(
"function extend(x, y) {}" +
"/** @constructor */ function Foo() {}" +
"extend(Foo, {bar: 1});" +
"alert(Foo.bar);",
"Property bar never defined on Foo");
}
public void testLends4() throws Exception {
testTypes(
"function extend(x, y) {}" +
"/** @constructor */ function Foo() {}" +
"extend(Foo, /** @lends {Foo} */ ({bar: 1}));" +
"alert(Foo.bar);");
}
public void testLends5() throws Exception {
testTypes(
"function extend(x, y) {}" +
"/** @constructor */ function Foo() {}" +
"extend(Foo, {bar: 1});" +
"alert((new Foo()).bar);",
"Property bar never defined on Foo");
}
public void testLends6() throws Exception {
testTypes(
"function extend(x, y) {}" +
"/** @constructor */ function Foo() {}" +
"extend(Foo, /** @lends {Foo.prototype} */ ({bar: 1}));" +
"alert((new Foo()).bar);");
}
public void testLends7() throws Exception {
testTypes(
"function extend(x, y) {}" +
"/** @constructor */ function Foo() {}" +
"extend(Foo, /** @lends {Foo.prototype|Foo} */ ({bar: 1}));",
"Parse error. expected closing }");
}
public void testLends8() throws Exception {
testTypes(
"function extend(x, y) {}" +
"/** @type {number} */ var Foo = 3;" +
"extend(Foo, /** @lends {Foo} */ ({bar: 1}));",
"May only lend properties to object types. Foo has type number.");
}
public void testLends9() throws Exception {
testClosureTypesMultipleWarnings(
"function extend(x, y) {}" +
"/** @constructor */ function Foo() {}" +
"extend(Foo, /** @lends {!Foo} */ ({bar: 1}));",
Lists.newArrayList(
"Parse error. expected closing }",
"Parse error. missing object name in @lends tag"));
}
public void testDeclaredNativeTypeEquality() throws Exception {
Node n = parseAndTypeCheck("/** @constructor */ function Object() {};");
assertEquals(registry.getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE),
n.getFirstChild().getJSType());
}
public void testUndefinedVar() throws Exception {
Node n = parseAndTypeCheck("var undefined;");
assertEquals(registry.getNativeType(JSTypeNative.VOID_
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>TYPE),
n.getFirstChild().getFirstChild().getJSType());
}
public void testFlowScopeBug1() throws Exception {
Node n = parseAndTypeCheck("/** @param {number} a \n"
+ "* @param {number} b */\n"
+ "function f(a, b) {\n"
+ "/** @type number */"
+ "var i = 0;"
+ "for (; (i + a) < b; ++i) {}}");
// check the type of the add node for i + f
assertEquals(registry.getNativeType(JSTypeNative.NUMBER_TYPE),
n.getFirstChild().getLastChild().getLastChild().getFirstChild()
.getNext().getFirstChild().getJSType());
}
public void testFlowScopeBug2() throws Exception {
Node n = parseAndTypeCheck("/** @constructor */ function Foo() {};\n"
+ "Foo.prototype.hi = false;"
+ "function foo(a, b) {\n"
+ " /** @type Array */"
+ " var arr;"
+ " /** @type number */"
+ " var iter;"
+ " for (iter = 0; iter < arr.length; ++ iter) {"
+ " /** @type Foo */"
+ " var afoo = arr[iter];"
+ " afoo;"
+ " }"
+ "}");
// check the type of afoo when referenced
assertEquals(registry.createNullableType(registry.getType("Foo")),
n.getLastChild().getLastChild().getLastChild().getLastChild()
.getLastChild().getLastChild().getJSType());
}
public void testAddSingletonGetter() {
Node n = parseAndTypeCheck(
"/** @constructor */ function Foo() {};\n" +
"goog.addSingletonGetter(Foo);");
ObjectType o = (ObjectType) n.getFirstChild().getJSType();
assertEquals("function (): Foo",
o.getPropertyType("getInstance").toString());
assertEquals("Foo", o.getPropertyType("instance_").toString());
}
public void testTypeCheckStandaloneAST() throws Exception {
Node n = compiler.parseTestCode("function Foo() { }");
typeCheck(n);
TypedScopeCreator scopeCreator = new TypedScopeCreator(compiler);
Scope topScope = scopeCreator.createScope(n, null);
Node second = compiler.parseTestCode("new Foo");
Node externs = new Node(Token.BLOCK);
Node externAndJsRoot = new Node(Token.BLOCK, externs, second);
externAndJsRoot.setIsSyntheticBlock(true);
new TypeCheck(
compiler,
new SemanticReverseAbstractInterpreter(
compiler.getCodingConvention(), registry),
registry, topScope, scopeCreator, CheckLevel.WARNING, CheckLevel.OFF)
.process(null, second);
assertEquals(1, compiler.getWarningCount());
assertEquals("cannot instantiate non-constructor",
compiler.getWarnings()[0].description);
}
public void testUpdateParameterTypeOnClosure() throws Exception {
testTypes(
"/**\n" +
"* @constructor\n" +
"* @param {*=} opt_value\n" +
"* @return {?}\n" +
"*/\n" +
"function Object(opt_value) {}\n" +
"/**\n" +
"* @constructor\n" +
"* @param {...*} var_args\n" +
"*/\n" +
"function Function(var_args) {}\n" +
"/**\n" +
"* @type {Function}\n" +
"*/\n"
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> testFunctionLiteralUnreadThisArgument() throws Exception {
testTypes(""
+ "/**\n"
+ " * @param {function(this:T, ...)?} fn\n"
+ " * @param {?T} opt_obj\n"
+ " * @template T\n"
+ " */\n"
+ "function baz(fn, opt_obj) {}\n"
+ "baz(function() {}, {});",
"Function literal argument does not refer to bound this argument");
}
public void testFunctionLiteralUnreadNullThisArgument() throws Exception {
testTypes(""
+ "/**\n"
+ " * @param {function(this:T, ...)?} fn\n"
+ " * @param {?T} opt_obj\n"
+ " * @template T\n"
+ " */\n"
+ "function baz(fn, opt_obj) {}\n"
+ "baz(function() {}, null);");
}
public void testActiveXObject() throws Exception {
testTypes(
"/** @type {Object} */ var x = new ActiveXObject();" +
"/** @type { {impossibleProperty} } */ var y = new ActiveXObject();");
}
private void checkObjectType(ObjectType objectType, String propertyName,
JSType expectedType) {
assertTrue("Expected " + objectType.getReferenceName() +
" to have property " +
propertyName, objectType.hasProperty(propertyName));
assertEquals("Expected " + objectType.getReferenceName() +
"'s property " +
propertyName + " to have type " + expectedType,
expectedType, objectType.getPropertyType(propertyName));
}
private void testTypes(String js) throws Exception {
testTypes(js, (String) null);
}
private void testTypes(String js, String description) throws Exception {
testTypes(js, description, false);
}
private void testTypes(String js, DiagnosticType type) throws Exception {
testTypes(js, type.format(), false);
}
private void testClosureTypes(String js, String description)
throws Exception {
testClosureTypesMultipleWarnings(js,
description == null ? null : Lists.newArrayList(description));
}
private void testClosureTypesMultipleWarnings(
String js, List<String> descriptions) throws Exception {
Node n = compiler.parseTestCode(js);
Node externs = new Node(Token.BLOCK);
Node externAndJsRoot = new Node(Token.BLOCK, externs, n);
externAndJsRoot.setIsSyntheticBlock(true);
assertEquals("parsing error: " +
Joiner.on(", ").join(compiler.getErrors()),
0, compiler.getErrorCount());
// For processing goog.addDependency for forward typedefs.
new ProcessClosurePrimitives(compiler, CheckLevel.ERROR, true)
.process(null, n);
CodingConvention convention = compiler.getCodingConvention();
new TypeCheck(compiler,
new ClosureReverseAbstractInterpreter(
convention, registry).append(
new SemanticReverseAbstractInterpreter(
convention, registry))
.getFirst(),
registry)
.processForTesting(null, n);
assertEquals(0, compiler.getErrorCount());
if (descriptions == null) {
assertEquals(
"unexpected warning(s) : " +
Joiner.on(", ").join(compiler.getWarnings()),
0, compiler.getWarningCount());
} else {
assertEquals(
"unexpected warning(s) : " +
Joiner.on(", ").join(compiler.getWarnings()),
descriptions.size(), compiler.getWarningCount());
for (int i = 0; i < descriptions.size(); i++)
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> {
assertEquals(descriptions.get(i),
compiler.getWarnings()[i].description);
}
}
}
void testTypes(String js, String description, boolean isError)
throws Exception {
testTypes(DEFAULT_EXTERNS, js, description, isError);
}
void testTypes(String externs, String js, String description, boolean isError)
throws Exception {
Node n = parseAndTypeCheck(externs, js);
JSError[] errors = compiler.getErrors();
if (description != null && isError) {
assertTrue("expected an error", errors.length > 0);
assertEquals(description, errors[0].description);
errors = Arrays.asList(errors).subList(1, errors.length).toArray(
new JSError[errors.length - 1]);
}
if (errors.length > 0) {
fail("unexpected error(s):\n" + Joiner.on("\n").join(errors));
}
JSError[] warnings = compiler.getWarnings();
if (description != null && !isError) {
assertTrue("expected a warning", warnings.length > 0);
assertEquals(description, warnings[0].description);
warnings = Arrays.asList(warnings).subList(1, warnings.length).toArray(
new JSError[warnings.length - 1]);
}
if (warnings.length > 0) {
fail("unexpected warnings(s):\n" + Joiner.on("\n").join(warnings));
}
}
/**
* Parses and type checks the JavaScript code.
*/
private Node parseAndTypeCheck(String js) {
return parseAndTypeCheck(DEFAULT_EXTERNS, js);
}
private Node parseAndTypeCheck(String externs, String js) {
return parseAndTypeCheckWithScope(externs, js).root;
}
/**
* Parses and type checks the JavaScript code and returns the Scope used
* whilst type checking.
*/
private TypeCheckResult parseAndTypeCheckWithScope(String js) {
return parseAndTypeCheckWithScope(DEFAULT_EXTERNS, js);
}
private TypeCheckResult parseAndTypeCheckWithScope(
String externs, String js) {
compiler.init(
Lists.newArrayList(JSSourceFile.fromCode("[externs]", externs)),
Lists.newArrayList(JSSourceFile.fromCode("[testcode]", js)),
compiler.getOptions());
Node n = compiler.getInput("[testcode]").getAstRoot(compiler);
Node externsNode = compiler.getInput("[externs]").getAstRoot(compiler);
Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n);
externAndJsRoot.setIsSyntheticBlock(true);
assertEquals("parsing error: " +
Joiner.on(", ").join(compiler.getErrors()),
0, compiler.getErrorCount());
Scope s = makeTypeCheck().processForTesting(externsNode, n);
return new TypeCheckResult(n, s);
}
private Node typeCheck(Node n) {
Node externsNode = new Node(Token.BLOCK);
Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n);
externAndJsRoot.setIsSyntheticBlock(true);
makeTypeCheck().processForTesting(null, n);
return n;
}
private TypeCheck makeTypeCheck() {
return new TypeCheck(
compiler,
new SemanticReverseAbstractInterpreter(
compiler.getCodingConvention(), registry),
registry,
reportMissingOverrides,
CheckLevel.OFF);
}
void testTypes(String js, String[] warnings) throws Exception {
Node n =
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.TRUE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
/**
* Null type.
*/
public final class NullType extends ValueType {
private static final long serialVersionUID = 1L;
NullType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isNullType() {
return true;
}
@Override
public boolean isNullable() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return false;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public JSType restrictByNotNullOrUndefined() {
return registry.getNativeType(JSTypeNative.NO_TYPE);
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isNullType() || that.isVoidType()) {
return TRUE;
}
if (that.isUnknownType() || that.isNullable()) {
return UNKNOWN;
}
return FALSE;
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "null";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.FALSE;
}
@Override
public <T> T visit(Visitor<T> visitor)
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.jstype.ObjectType;
/**
* Represents the prototype of a {@link FunctionType}.
* @author nicksantos@google.com (Nick Santos)
*/
public class FunctionPrototypeType extends PrototypeObjectType {
private static final long serialVersionUID = 1L;
private final FunctionType ownerFunction;
FunctionPrototypeType(JSTypeRegistry registry, FunctionType ownerFunction,
ObjectType implicitPrototype, boolean isNative) {
super(registry, null /* has no class name */, implicitPrototype,
isNative);
this.ownerFunction = ownerFunction;
}
FunctionPrototypeType(JSTypeRegistry registry, FunctionType ownerFunction,
ObjectType implicitPrototype) {
this(registry, ownerFunction, implicitPrototype, false);
}
@Override
public String getReferenceName() {
if (ownerFunction == null) {
return "{...}.prototype";
} else {
return ownerFunction.getReferenceName() + ".prototype";
}
}
@Override
public boolean hasReferenceName() {
return ownerFunction != null && ownerFunction.hasReferenceName();
}
@Override
public boolean isFunctionPrototypeType() {
return true;
}
public FunctionType getOwnerFunction() {
return ownerFunction;
}
@Override
public Iterable<ObjectType> getCtorImplementedInterfaces() {
return getOwnerFunction().getImplementedInterfaces();
}
// The owner will always be a resolved type, so there's no need to set
// the ownerFunction in resolveInternal.
// (it would lead to infinite loops if we did).
// JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope);
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> names in the expression point to
* unknown types, then we create a proxy {@code NamedType} structure
* until the type can be resolved.
*
* This is the legacy way of resolving ways, and may not exist in the
* future.
*/
LAZY_NAMES,
/**
* Expressions and type names are evaluated aggressively. A warning
* will be emitted if a type name fails to resolve to a real type.
*/
IMMEDIATE
}
private ResolveMode resolveMode = ResolveMode.LAZY_NAMES;
/**
* Constructs a new type registry populated with the built-in types.
*/
public JSTypeRegistry(ErrorReporter reporter) {
this(reporter, false);
}
/**
* Constructs a new type registry populated with the built-in types.
*/
public JSTypeRegistry(
ErrorReporter reporter, boolean tolerateUndefinedValues) {
this.reporter = reporter;
nativeTypes = new JSType[JSTypeNative.values().length];
namesToTypes = new HashMap<String, JSType>();
resetForTypeCheck();
this.tolerateUndefinedValues = tolerateUndefinedValues;
}
/**
* Set the current resolving mode of the type registry.
* @see ResolveMode
*/
public void setResolveMode(ResolveMode mode) {
this.resolveMode = mode;
}
ResolveMode getResolveMode() {
return resolveMode;
}
public ErrorReporter getErrorReporter() {
return reporter;
}
public boolean shouldTolerateUndefinedValues() {
return tolerateUndefinedValues;
}
/**
* Reset to run the TypeCheck pass.
*/
public void resetForTypeCheck() {
typesIndexedByProperty.clear();
eachRefTypeIndexedByProperty.clear();
initializeBuiltInTypes();
namesToTypes.clear();
namespaces.clear();
initializeRegistry();
}
private void initializeBuiltInTypes() {
// These locals shouldn't be all caps.
BooleanType BOOLEAN_TYPE = new BooleanType(this);
registerNativeType(JSTypeNative.BOOLEAN_TYPE, BOOLEAN_TYPE);
NullType NULL_TYPE = new NullType(this);
registerNativeType(JSTypeNative.NULL_TYPE, NULL_TYPE);
NumberType NUMBER_TYPE = new NumberType(this);
registerNativeType(JSTypeNative.NUMBER_TYPE, NUMBER_TYPE);
StringType STRING_TYPE = new StringType(this);
registerNativeType(JSTypeNative.STRING_TYPE, STRING_TYPE);
UnknownType UNKNOWN_TYPE = new UnknownType(this, false);
registerNativeType(JSTypeNative.UNKNOWN_TYPE, UNKNOWN_TYPE);
registerNativeType(
JSTypeNative.CHECKED_UNKNOWN_TYPE, new UnknownType(this, true));
VoidType VOID_TYPE = new VoidType(this);
registerNativeType(JSTypeNative.VOID_TYPE, VOID_TYPE);
AllType ALL_TYPE = new AllType(this);
registerNativeType(JSTypeNative.ALL_TYPE, ALL_TYPE);
// Top Level Prototype (the One)
// The initializations of TOP_LEVEL_PROTOTYPE and OBJECT_FUNCTION_TYPE
// use each other's results, so at least one of them will get null
// instead of an actual type; however, this seems to be benign.
ObjectType TOP_LEVEL_PROTOTYPE =
new FunctionPrototypeType(this, null, null, true);
registerNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE, TOP_LEVEL_PROTOTYPE);
// Object
FunctionType OBJECT_FUNCTION_TYPE =
new FunctionType(this, "Object", null,
createArrowType(createOptionalParameters(ALL_TYPE), UNKNOWN
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>_TYPE),
null, null, true, true);
OBJECT_FUNCTION_TYPE.defineDeclaredProperty(
"prototype", TOP_LEVEL_PROTOTYPE, true, null);
registerNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE, OBJECT_FUNCTION_TYPE);
ObjectType OBJECT_PROTOTYPE = OBJECT_FUNCTION_TYPE.getPrototype();
registerNativeType(JSTypeNative.OBJECT_PROTOTYPE, OBJECT_PROTOTYPE);
ObjectType OBJECT_TYPE = OBJECT_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.OBJECT_TYPE, OBJECT_TYPE);
// Function
FunctionType FUNCTION_FUNCTION_TYPE =
new FunctionType(this, "Function", null,
createArrowType(
createParametersWithVarArgs(ALL_TYPE), UNKNOWN_TYPE),
null, null, true, true);
FUNCTION_FUNCTION_TYPE.setPrototypeBasedOn(OBJECT_TYPE);
registerNativeType(
JSTypeNative.FUNCTION_FUNCTION_TYPE, FUNCTION_FUNCTION_TYPE);
ObjectType FUNCTION_PROTOTYPE = FUNCTION_FUNCTION_TYPE.getPrototype();
registerNativeType(JSTypeNative.FUNCTION_PROTOTYPE, FUNCTION_PROTOTYPE);
NoType NO_TYPE = new NoType(this);
registerNativeType(JSTypeNative.NO_TYPE, NO_TYPE);
NoObjectType NO_OBJECT_TYPE = new NoObjectType(this);
registerNativeType(JSTypeNative.NO_OBJECT_TYPE, NO_OBJECT_TYPE);
NoObjectType NO_RESOLVED_TYPE = new NoResolvedType(this);
registerNativeType(JSTypeNative.NO_RESOLVED_TYPE, NO_RESOLVED_TYPE);
// Array
FunctionType ARRAY_FUNCTION_TYPE =
new FunctionType(this, "Array", null,
createArrowType(createParametersWithVarArgs(ALL_TYPE), null),
null, null, true, true);
ARRAY_FUNCTION_TYPE.getInternalArrowType().returnType =
ARRAY_FUNCTION_TYPE.getInstanceType();
ObjectType arrayPrototype = ARRAY_FUNCTION_TYPE.getPrototype();
registerNativeType(JSTypeNative.ARRAY_FUNCTION_TYPE, ARRAY_FUNCTION_TYPE);
ObjectType ARRAY_TYPE = ARRAY_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.ARRAY_TYPE, ARRAY_TYPE);
// Boolean
FunctionType BOOLEAN_OBJECT_FUNCTION_TYPE =
new FunctionType(this, "Boolean", null,
createArrowType(createParameters(false, ALL_TYPE), BOOLEAN_TYPE),
null, null, true, true);
ObjectType booleanPrototype = BOOLEAN_OBJECT_FUNCTION_TYPE.getPrototype();
registerNativeType(
JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE, BOOLEAN_OBJECT_FUNCTION_TYPE);
ObjectType BOOLEAN_OBJECT_TYPE =
BOOLEAN_OBJECT_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE, BOOLEAN_OBJECT_TYPE);
// Date
FunctionType DATE_FUNCTION_TYPE =
new FunctionType(this, "Date", null,
createArrowType(
createOptionalParameters(UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE,
UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE),
STRING_TYPE),
null, null, true, true);
ObjectType datePrototype = DATE_FUNCTION_TYPE.getPrototype();
registerNativeType(JSTypeNative.DATE_FUNCTION_TYPE, DATE_FUNCTION_TYPE);
ObjectType DATE_TYPE = DATE_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.DATE_TYPE, DATE_TYPE);
// Error
FunctionType ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "Error");
registerNativeType(JSType
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>Native.ERROR_FUNCTION_TYPE, ERROR_FUNCTION_TYPE);
ObjectType ERROR_TYPE = ERROR_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.ERROR_TYPE, ERROR_TYPE);
// EvalError
FunctionType EVAL_ERROR_FUNCTION_TYPE =
new ErrorFunctionType(this, "EvalError");
EVAL_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE);
registerNativeType(
JSTypeNative.EVAL_ERROR_FUNCTION_TYPE, EVAL_ERROR_FUNCTION_TYPE);
ObjectType EVAL_ERROR_TYPE = EVAL_ERROR_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.EVAL_ERROR_TYPE, EVAL_ERROR_TYPE);
// RangeError
FunctionType RANGE_ERROR_FUNCTION_TYPE =
new ErrorFunctionType(this, "RangeError");
RANGE_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE);
registerNativeType(
JSTypeNative.RANGE_ERROR_FUNCTION_TYPE, RANGE_ERROR_FUNCTION_TYPE);
ObjectType RANGE_ERROR_TYPE = RANGE_ERROR_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.RANGE_ERROR_TYPE, RANGE_ERROR_TYPE);
// ReferenceError
FunctionType REFERENCE_ERROR_FUNCTION_TYPE =
new ErrorFunctionType(this, "ReferenceError");
REFERENCE_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE);
registerNativeType(
JSTypeNative.REFERENCE_ERROR_FUNCTION_TYPE, REFERENCE_ERROR_FUNCTION_TYPE);
ObjectType REFERENCE_ERROR_TYPE =
REFERENCE_ERROR_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.REFERENCE_ERROR_TYPE, REFERENCE_ERROR_TYPE);
// SyntaxError
FunctionType SYNTAX_ERROR_FUNCTION_TYPE =
new ErrorFunctionType(this, "SyntaxError");
SYNTAX_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE);
registerNativeType(
JSTypeNative.SYNTAX_ERROR_FUNCTION_TYPE, SYNTAX_ERROR_FUNCTION_TYPE);
ObjectType SYNTAX_ERROR_TYPE = SYNTAX_ERROR_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.SYNTAX_ERROR_TYPE, SYNTAX_ERROR_TYPE);
// TypeError
FunctionType TYPE_ERROR_FUNCTION_TYPE =
new ErrorFunctionType(this, "TypeError");
TYPE_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE);
registerNativeType(
JSTypeNative.TYPE_ERROR_FUNCTION_TYPE, TYPE_ERROR_FUNCTION_TYPE);
ObjectType TYPE_ERROR_TYPE = TYPE_ERROR_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.TYPE_ERROR_TYPE, TYPE_ERROR_TYPE);
// URIError
FunctionType URI_ERROR_FUNCTION_TYPE =
new ErrorFunctionType(this, "URIError");
URI_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE);
registerNativeType(
JSTypeNative.URI_ERROR_FUNCTION_TYPE, URI_ERROR_FUNCTION_TYPE);
ObjectType URI_ERROR_TYPE = URI_ERROR_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.URI_ERROR_TYPE, URI_ERROR_TYPE);
// Number
FunctionType NUMBER_OBJECT_FUNCTION_TYPE =
new FunctionType(this, "Number", null,
createArrowType(createParameters(false, ALL_TYPE), NUMBER_TYPE),
null, null, true, true);
ObjectType numberPrototype = NUMBER_OBJECT_FUNCTION_TYPE.getPrototype();
registerNativeType(
JSTypeNative.NUMBER_OBJECT_FUNCTION
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>_TYPE, NUMBER_OBJECT_FUNCTION_TYPE);
ObjectType NUMBER_OBJECT_TYPE =
NUMBER_OBJECT_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.NUMBER_OBJECT_TYPE, NUMBER_OBJECT_TYPE);
// RegExp
FunctionType REGEXP_FUNCTION_TYPE =
new FunctionType(this, "RegExp", null,
createArrowType(createOptionalParameters(ALL_TYPE, ALL_TYPE)),
null, null, true, true);
REGEXP_FUNCTION_TYPE.getInternalArrowType().returnType =
REGEXP_FUNCTION_TYPE.getInstanceType();
ObjectType regexpPrototype = REGEXP_FUNCTION_TYPE.getPrototype();
registerNativeType(JSTypeNative.REGEXP_FUNCTION_TYPE, REGEXP_FUNCTION_TYPE);
ObjectType REGEXP_TYPE = REGEXP_FUNCTION_TYPE.getInstanceType();
registerNativeType(JSTypeNative.REGEXP_TYPE, REGEXP_TYPE);
// String
FunctionType STRING_OBJECT_FUNCTION_TYPE =
new FunctionType(this, "String", null,
createArrowType(createParameters(false, ALL_TYPE), STRING_TYPE),
null, null, true, true);
ObjectType stringPrototype = STRING_OBJECT_FUNCTION_TYPE.getPrototype();
registerNativeType(
JSTypeNative.STRING_OBJECT_FUNCTION_TYPE, STRING_OBJECT_FUNCTION_TYPE);
ObjectType STRING_OBJECT_TYPE =
STRING_OBJECT_FUNCTION_TYPE.getInstanceType();
registerNativeType(
JSTypeNative.STRING_OBJECT_TYPE, STRING_OBJECT_TYPE);
// (Object,string,number)
JSType OBJECT_NUMBER_STRING =
createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE);
registerNativeType(JSTypeNative.OBJECT_NUMBER_STRING, OBJECT_NUMBER_STRING);
// (Object,string,number,boolean)
JSType OBJECT_NUMBER_STRING_BOOLEAN =
createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE);
registerNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN,
OBJECT_NUMBER_STRING_BOOLEAN);
// (string,number,boolean)
JSType NUMBER_STRING_BOOLEAN =
createUnionType(NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE);
registerNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN,
NUMBER_STRING_BOOLEAN);
// (string,number)
JSType NUMBER_STRING = createUnionType(NUMBER_TYPE, STRING_TYPE);
registerNativeType(JSTypeNative.NUMBER_STRING, NUMBER_STRING);
// Native object properties are filled in by externs...
// (String, string)
JSType STRING_VALUE_OR_OBJECT_TYPE =
createUnionType(STRING_OBJECT_TYPE, STRING_TYPE);
registerNativeType(
JSTypeNative.STRING_VALUE_OR_OBJECT_TYPE, STRING_VALUE_OR_OBJECT_TYPE);
// (Number, number)
JSType NUMBER_VALUE_OR_OBJECT_TYPE =
createUnionType(NUMBER_OBJECT_TYPE, NUMBER_TYPE);
registerNativeType(
JSTypeNative.NUMBER_VALUE_OR_OBJECT_TYPE, NUMBER_VALUE_OR_OBJECT_TYPE);
// unknown function type, i.e. (?...) -> ?
FunctionType U2U_FUNCTION_TYPE =
createFunctionType(UNKNOWN_TYPE, true, UNKNOWN_TYPE);
registerNativeType(JSTypeNative.U2U_FUNCTION_TYPE, U2U_FUNCTION_TYPE);
// unknown constructor type, i.e. (?...) -> ? with the NoObject type
// as instance type
FunctionType U2U_CONSTRUCTOR_TYPE =
// This is equivalent to
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> // createConstructorType(UNKNOWN_TYPE, true, UNKNOWN_TYPE), but,
// in addition, overrides getInstanceType() to return the NoObject type
// instead of a new anonymous object.
new FunctionType(this, "Function", null,
createArrowType(
createParametersWithVarArgs(UNKNOWN_TYPE),
UNKNOWN_TYPE),
NO_OBJECT_TYPE, null, true, true) {
private static final long serialVersionUID = 1L;
@Override public FunctionType getConstructor() {
return registry.getNativeFunctionType(
JSTypeNative.FUNCTION_FUNCTION_TYPE);
}
};
// The U2U_CONSTRUCTOR is weird, because it's the supertype of its
// own constructor.
registerNativeType(JSTypeNative.U2U_CONSTRUCTOR_TYPE, U2U_CONSTRUCTOR_TYPE);
registerNativeType(
JSTypeNative.FUNCTION_INSTANCE_TYPE, U2U_CONSTRUCTOR_TYPE);
FUNCTION_FUNCTION_TYPE.setInstanceType(U2U_CONSTRUCTOR_TYPE);
U2U_CONSTRUCTOR_TYPE.setImplicitPrototype(FUNCTION_PROTOTYPE);
// least function type, i.e. (All...) -> NoType
FunctionType LEAST_FUNCTION_TYPE =
createFunctionType(NO_TYPE, true, ALL_TYPE);
registerNativeType(JSTypeNative.LEAST_FUNCTION_TYPE, LEAST_FUNCTION_TYPE);
// the 'this' object in the global scope
ObjectType GLOBAL_THIS = createObjectType("global this", null,
UNKNOWN_TYPE /* to be resolved later */);
registerNativeType(JSTypeNative.GLOBAL_THIS, GLOBAL_THIS);
// greatest function type, i.e. (NoType...) -> All
FunctionType GREATEST_FUNCTION_TYPE =
createFunctionType(ALL_TYPE, true, NO_TYPE);
registerNativeType(JSTypeNative.GREATEST_FUNCTION_TYPE,
GREATEST_FUNCTION_TYPE);
// Register the prototype property. See the comments below in
// registerPropertyOnType about the bootstrapping process.
registerPropertyOnType("prototype", OBJECT_FUNCTION_TYPE);
}
private void initializeRegistry() {
register(getNativeType(JSTypeNative.ARRAY_TYPE));
register(getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE));
register(getNativeType(JSTypeNative.BOOLEAN_TYPE));
register(getNativeType(JSTypeNative.DATE_TYPE));
register(getNativeType(JSTypeNative.NULL_TYPE));
register(getNativeType(JSTypeNative.NULL_TYPE), "Null");
register(getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE));
register(getNativeType(JSTypeNative.NUMBER_TYPE));
register(getNativeType(JSTypeNative.OBJECT_TYPE));
register(getNativeType(JSTypeNative.ERROR_TYPE));
register(getNativeType(JSTypeNative.URI_ERROR_TYPE));
register(getNativeType(JSTypeNative.EVAL_ERROR_TYPE));
register(getNativeType(JSTypeNative.TYPE_ERROR_TYPE));
register(getNativeType(JSTypeNative.RANGE_ERROR_TYPE));
register(getNativeType(JSTypeNative.REFERENCE_ERROR_TYPE));
register(getNativeType(JSTypeNative.SYNTAX_ERROR_TYPE));
register(getNativeType(JSTypeNative.REGEXP_TYPE));
register(getNativeType(JSTypeNative.STRING_OBJECT_TYPE));
register(getNativeType(JSTypeNative.STRING_TYPE));
register(getNativeType(JSTypeNative.VOID_TYPE));
register(getNativeType(JSType
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>Native.VOID_TYPE), "Undefined");
register(getNativeType(JSTypeNative.VOID_TYPE), "void");
register(getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), "Function");
}
private void register(JSType type) {
register(type, type.toString());
}
private void register(JSType type, String name) {
namesToTypes.put(name, type);
// Add all the namespaces in which this name lives.
while (name.indexOf('.') > 0) {
name = name.substring(0, name.lastIndexOf('.'));
namespaces.add(name);
}
}
private void registerNativeType(JSTypeNative typeId, JSType type) {
nativeTypes[typeId.ordinal()] = type;
}
/**
* Tells the type system that {@code owner} may have a property named
* {@code propertyName}. This allows the registry to keep track of what
* types a property is defined upon.
*
* This is NOT the same as saying that {@code owner} must have a property
* named type. ObjectType#hasProperty attempts to minimize false positives
* ("if we're not sure, then don't type check this property"). The type
* registry, on the other hand, should attempt to minimize false negatives
* ("if this property is assigned anywhere in the program, it must
* show up in the type registry").
*/
public void registerPropertyOnType(String propertyName, JSType type) {
UnionTypeBuilder typeSet = typesIndexedByProperty.get(propertyName);
if (typeSet == null) {
typeSet = new UnionTypeBuilder(this);
typesIndexedByProperty.put(propertyName, typeSet);
}
typeSet.addAlternate(type);
addReferenceTypeIndexedByProperty(propertyName, type);
// Clear cached values that depend on typesIndexedByProperty.
greatestSubtypeByProperty.remove(propertyName);
}
private void addReferenceTypeIndexedByProperty(
String propertyName, JSType type) {
if (type instanceof ObjectType && ((ObjectType) type).hasReferenceName()) {
Map<String, ObjectType> typeSet =
eachRefTypeIndexedByProperty.get(propertyName);
if (typeSet == null) {
typeSet = Maps.newHashMap();
eachRefTypeIndexedByProperty.put(propertyName, typeSet);
}
ObjectType objType = (ObjectType) type;
typeSet.put(objType.getReferenceName(), objType);
} else if (type instanceof NamedType) {
addReferenceTypeIndexedByProperty(
propertyName, ((NamedType) type).getReferencedType());
} else if (type instanceof UnionType) {
for (JSType alternate : ((UnionType) type).getAlternates()) {
addReferenceTypeIndexedByProperty(propertyName, alternate);
}
}
}
/**
* Gets the greatest subtype of the {@code type} that has a property
* {@code propertyName} defined on it.
*/
public JSType getGreatestSubtypeWithProperty(
JSType type, String propertyName) {
if (greatestSubtypeByProperty.containsKey(propertyName)) {
return greatestSubtypeByProperty.get(propertyName)
.getGreatestSubtype(type);
}
if (typesIndexedByProperty.containsKey(propertyName)) {
JSType built = typesIndexedByProperty.get(propertyName).build();
greatestSubtypeByProperty.put(propertyName, built);
return built.getGreatestSubtype(type);
}
return getNativeType(NO_TYPE);
}
/**
* Returns whether the given property can possibly be set on the given type.
*/
public boolean canPropertyBeDefined(JSType type, String propertyName) {
if
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>(name)) {
return false;
}
register(t, name);
return true;
}
/**
* Overrides a declared global type name. Throws an exception if this
* type name hasn't been declared yet.
*/
public void overwriteDeclaredType(String name, JSType t) {
Preconditions.checkState(namesToTypes.containsKey(name));
register(t, name);
}
/**
* Records a forward-declared type name. We will not emit errors if this
* type name never resolves to anything.
*/
public void forwardDeclareType(String name) {
forwardDeclaredTypes.add(name);
}
/**
* Whether this is a forward-declared type name.
*/
public boolean isForwardDeclaredType(String name) {
return forwardDeclaredTypes.contains(name);
}
/** Determines whether the given JS package exists. */
public boolean hasNamespace(String name) {
return namespaces.contains(name);
}
/**
* Looks up a type by name.
*
* @param jsTypeName The name string.
* @return the corresponding JSType object or {@code null} it cannot be found
*/
public JSType getType(String jsTypeName) {
// TODO(user): Push every local type name out of namesToTypes so that
// NamedType#resolve is correct.
if (jsTypeName.equals(templateTypeName)) {
return templateType;
}
return namesToTypes.get(jsTypeName);
}
public JSType getNativeType(JSTypeNative typeId) {
return nativeTypes[typeId.ordinal()];
}
public ObjectType getNativeObjectType(JSTypeNative typeId) {
return (ObjectType) getNativeType(typeId);
}
public FunctionType getNativeFunctionType(JSTypeNative typeId) {
return (FunctionType) getNativeType(typeId);
}
/**
* Try to resolve a type name, but forgive the user and don't emit
* a warning if this doesn't resolve.
*/
public JSType getForgivingType(StaticScope<JSType> scope, String jsTypeName,
String sourceName, int lineno, int charno) {
JSType type = getType(
scope, jsTypeName, sourceName, lineno, charno);
type.forgiveUnknownNames();
return type;
}
/**
* Looks up a type by name. To allow for forward references to types, an
* unrecognized string has to be bound to a NamedType object that will be
* resolved later.
*
* @param scope A scope for doing type name resolution.
* @param jsTypeName The name string.
* @param sourceName The name of the source file where this reference appears.
* @param lineno The line number of the reference.
* @return a NamedType if the string argument is not one of the known types,
* otherwise the corresponding JSType object.
*/
public JSType getType(StaticScope<JSType> scope, String jsTypeName,
String sourceName, int lineno, int charno) {
JSType type = getType(jsTypeName);
if (type == null) {
// TODO(user): Each instance should support named type creation using
// interning.
NamedType namedType =
new NamedType(this, jsTypeName, sourceName, lineno, charno);
unresolvedNamedTypes.put(scope, namedType);
type = namedType;
}
return type;
}
/**
* Resolve all the unresolved types in the given scope.
*/
public void resolveTypesInScope(StaticScope<JSType> scope) {
for (NamedType type : unresolvedNamedTypes.get(scope)) {
type.resolve(reporter
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>, scope);
}
resolvedNamedTypes.putAll(scope, unresolvedNamedTypes.removeAll(scope));
if (scope != null && scope.getParentScope() == null) {
// By default, the global "this" type is just an anonymous object.
// If the user has defined a Window type, make the Window the
// implicit prototype of "this".
PrototypeObjectType globalThis = (PrototypeObjectType) getNativeType(
JSTypeNative.GLOBAL_THIS);
JSType windowType = getType("Window");
if (globalThis.isUnknownType()) {
ObjectType windowObjType = ObjectType.cast(windowType);
if (windowObjType != null) {
globalThis.setImplicitPrototype(windowObjType);
} else {
globalThis.setImplicitPrototype(
getNativeObjectType(JSTypeNative.OBJECT_TYPE));
}
}
}
}
/**
* Creates a type representing optional values of the given type.
* @return the union of the type and the void type
*/
public JSType createOptionalType(JSType type) {
if (type instanceof UnknownType || type.isAllType()) {
return type;
} else {
return createUnionType(type, getNativeType(JSTypeNative.VOID_TYPE));
}
}
/**
* Creates a type representing nullable values of the given type.
* @return the union of the type and the Null type
*/
public JSType createDefaultObjectUnion(JSType type) {
return shouldTolerateUndefinedValues()
? createOptionalNullableType(type)
: createNullableType(type);
}
/**
* Creates a type representing nullable values of the given type.
* @return the union of the type and the Null type
*/
public JSType createNullableType(JSType type) {
return createUnionType(type, getNativeType(JSTypeNative.NULL_TYPE));
}
/**
* Creates a nullabel and undefine-able value of the given type.
* @return The union of the type and null and undefined.
*/
public JSType createOptionalNullableType(JSType type) {
return createUnionType(type, getNativeType(JSTypeNative.VOID_TYPE),
getNativeType(JSTypeNative.NULL_TYPE));
}
/**
* Creates a union type whose variants are the arguments.
*/
public JSType createUnionType(JSType... variants) {
UnionTypeBuilder builder = new UnionTypeBuilder(this);
for (JSType type : variants) {
builder.addAlternate(type);
}
return builder.build();
}
/**
* Creates a union type whose variants are the builtin types specified
* by the arguments.
*/
public JSType createUnionType(JSTypeNative... variants) {
UnionTypeBuilder builder = new UnionTypeBuilder(this);
for (JSTypeNative typeId : variants) {
builder.addAlternate(getNativeType(typeId));
}
return builder.build();
}
/**
* Creates an enum type.
*/
public EnumType createEnumType(String name, JSType elementsType) {
return new EnumType(this, name, elementsType);
}
/**
* Creates an arrow type, an abstract representation of the parameters
* and return value of a function.
*
* @param parametersNode the parameters' types, formatted as a Node with
* param names and optionality info.
* @param returnType the function's return type
*/
ArrowType createArrowType(Node parametersNode, JSType returnType) {
return new ArrowType(this, parametersNode, returnType);
}
/**
* Creates an arrow type with an unknown return type.
*
*
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>, sourceName, scope, false);
if (arg.getType() == Token.EQUALS) {
boolean addSuccess = paramBuilder.addOptionalParams(type);
if (!addSuccess) {
reporter.warning(
ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"),
sourceName, arg.getLineno(), "", arg.getCharno());
}
} else {
paramBuilder.addRequiredParams(type);
}
}
}
current = current.getNext();
}
JSType returnType =
createFromTypeNodesInternal(current, sourceName, scope, false);
return new FunctionBuilder(this)
.withParams(paramBuilder)
.withReturnType(returnType)
.withTypeOfThis(thisType)
.setIsConstructor(isConstructor)
.build();
}
throw new IllegalStateException(
"Unexpected node in type expression: " + n.toString());
}
/**
* Creates a RecordType from the nodes representing said record type.
* @param n The node with type info.
* @param sourceName The source file name.
* @param scope A scope for doing type name lookups.
*/
private JSType createRecordTypeFromNodes(Node n, String sourceName,
StaticScope<JSType> scope) {
RecordTypeBuilder builder = new RecordTypeBuilder(this);
// For each of the fields in the record type.
for (Node fieldTypeNode = n.getFirstChild();
fieldTypeNode != null;
fieldTypeNode = fieldTypeNode.getNext()) {
// Get the property's name.
Node fieldNameNode = fieldTypeNode;
boolean hasType = false;
if (fieldTypeNode.getType() == Token.COLON) {
fieldNameNode = fieldTypeNode.getFirstChild();
hasType = true;
}
String fieldName = fieldNameNode.getString();
// TODO(user): Move this into the lexer/parser.
// Remove the string literal characters around a field name,
// if any.
if (fieldName.startsWith("'") || fieldName.startsWith("\"")) {
fieldName = fieldName.substring(1, fieldName.length() - 1);
}
// Get the property's type.
JSType fieldType = null;
if (hasType) {
// We have a declared type.
fieldType = createFromTypeNodesInternal(
fieldTypeNode.getLastChild(), sourceName, scope, false);
} else {
// Otherwise, the type is UNKNOWN.
fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
// Add the property to the record.
builder.addProperty(fieldName, fieldType, fieldNameNode);
}
return builder.build();
}
/**
* Sets the template type name.
*/
public void setTemplateTypeName(String name) {
templateTypeName = name;
templateType = new TemplateType(this, name);
}
/**
* Clears the template type name.
*/
public void clearTemplateTypeName() {
templateTypeName = null;
templateType = null;
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.Node;
/**
* The arrow type is an internal type that models the functional arrow type
* seen in typical functional programming languages. It is used soley for
* separating the management of the arrow type from the complex
* {@link FunctionType} that models JavaScript's notion of functions.
*/
final class ArrowType extends JSType {
private static final long serialVersionUID = 1L;
final Node parameters;
JSType returnType;
// Whether the return type is inferred.
final boolean returnTypeInferred;
ArrowType(JSTypeRegistry registry, Node parameters,
JSType returnType) {
this(registry, parameters, returnType, false);
}
ArrowType(JSTypeRegistry registry, Node parameters,
JSType returnType, boolean returnTypeInferred) {
super(registry);
this.parameters = parameters == null ?
registry.createParametersWithVarArgs(getNativeType(UNKNOWN_TYPE)) :
parameters;
this.returnType = returnType == null ?
getNativeType(UNKNOWN_TYPE) : returnType;
this.returnTypeInferred = returnTypeInferred;
}
@Override
public boolean isSubtype(JSType other) {
if (!(other instanceof ArrowType)) {
return false;
}
ArrowType that = (ArrowType) other;
// this.returnType <: that.returnType (covariant)
if (!this.returnType.isSubtype(that.returnType)) {
return false;
}
// that.paramType[i] <: this.paramType[i] (contravariant)
// TODO(nicksantos): This is incorrect. It should be
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.testing;
import com.google.javascript.rhino.JSTypeExpression;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.FunctionBuilder;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSTypeNative;
import com.google.javascript.rhino.jstype.JSTypeRegistry;
import com.google.javascript.rhino.jstype.ObjectType;
import com.google.javascript.rhino.jstype.RecordTypeBuilder;
import junit.framework.TestCase;
public abstract class BaseJSTypeTestCase extends TestCase {
protected JSTypeRegistry registry;
protected TestErrorReporter errorReporter;
protected JSType ALL_TYPE;
protected ObjectType NO_OBJECT_TYPE;
protected ObjectType NO_TYPE;
protected ObjectType NO_RESOLVED_TYPE;
protected JSType ARRAY_FUNCTION_TYPE;
protected ObjectType ARRAY_TYPE;
protected JSType BOOLEAN_OBJECT_FUNCTION_TYPE;
protected ObjectType BOOLEAN_OBJECT_TYPE;
protected JSType BOOLEAN_TYPE;
protected JSType CHECKED_UNKNOWN_TYPE;
protected JSType DATE_FUNCTION_TYPE;
protected ObjectType DATE_TYPE;
protected JSType ERROR_FUNCTION_TYPE;
protected ObjectType ERROR_TYPE;
protected JSType EVAL_ERROR_FUNCTION_TYPE;
protected ObjectType EVAL_ERROR_TYPE;
protected FunctionType FUNCTION_FUNCTION_TYPE;
protected FunctionType FUNCTION_INSTANCE_TYPE;
protected ObjectType FUNCTION_PROTOTYPE;
protected JSType GREATEST_FUNCTION_TYPE;
protected JSType LEAST_FUNCTION_TYPE;
protected JSType MATH_TYPE;
protected JSType NULL
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>_TYPE;
protected JSType NUMBER_OBJECT_FUNCTION_TYPE;
protected ObjectType NUMBER_OBJECT_TYPE;
protected JSType NUMBER_STRING_BOOLEAN;
protected JSType NUMBER_TYPE;
protected FunctionType OBJECT_FUNCTION_TYPE;
protected JSType OBJECT_NUMBER_STRING;
protected JSType OBJECT_NUMBER_STRING_BOOLEAN;
protected JSType OBJECT_PROTOTYPE;
protected ObjectType OBJECT_TYPE;
protected JSType RANGE_ERROR_FUNCTION_TYPE;
protected ObjectType RANGE_ERROR_TYPE;
protected JSType REFERENCE_ERROR_FUNCTION_TYPE;
protected ObjectType REFERENCE_ERROR_TYPE;
protected JSType REGEXP_FUNCTION_TYPE;
protected ObjectType REGEXP_TYPE;
protected JSType STRING_OBJECT_FUNCTION_TYPE;
protected ObjectType STRING_OBJECT_TYPE;
protected JSType STRING_TYPE;
protected JSType SYNTAX_ERROR_FUNCTION_TYPE;
protected ObjectType SYNTAX_ERROR_TYPE;
protected JSType TYPE_ERROR_FUNCTION_TYPE;
protected ObjectType TYPE_ERROR_TYPE;
protected FunctionType U2U_CONSTRUCTOR_TYPE;
protected FunctionType U2U_FUNCTION_TYPE;
protected ObjectType UNKNOWN_TYPE;
protected JSType URI_ERROR_FUNCTION_TYPE;
protected ObjectType URI_ERROR_TYPE;
protected JSType VOID_TYPE;
protected int NATIVE_PROPERTIES_COUNT;
@Override
protected void setUp() throws Exception {
super.setUp();
errorReporter = new TestErrorReporter(null, null);
registry = new JSTypeRegistry(errorReporter);
initTypes();
}
protected void initTypes() {
ALL_TYPE =
registry.getNativeType(JSTypeNative.ALL_TYPE);
NO_OBJECT_TYPE =
registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE);
NO_TYPE =
registry.getNativeObjectType(JSTypeNative.NO_TYPE);
NO_RESOLVED_TYPE =
registry.getNativeObjectType(JSTypeNative.NO_RESOLVED_TYPE);
ARRAY_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.ARRAY_FUNCTION_TYPE);
ARRAY_TYPE =
registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE);
BOOLEAN_OBJECT_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE);
BOOLEAN_OBJECT_TYPE =
registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE);
BOOLEAN_TYPE =
registry.getNativeType(JSTypeNative.BOOLEAN_TYPE);
CHECKED_UNKNOWN_TYPE =
registry.getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
DATE_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.DATE_FUNCTION_TYPE);
DATE_TYPE =
registry.getNativeObjectType(JSTypeNative.DATE_TYPE);
ERROR_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.ERROR_FUNCTION_TYPE);
ERROR_TYPE =
registry.getNativeObjectType(JSTypeNative.ERROR_TYPE);
EVAL_ERROR_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.EVAL_ERROR_FUNCTION_TYPE);
EVAL_ERROR_TYPE =
registry.getNativeObjectType(JSTypeNative.EVAL_ERROR_TYPE);
FUNCTION_FUNCTION_TYPE =
registry.getNativeFunctionType(JSTypeNative.FUNCTION_FUNCTION_TYPE);
FUNCTION_INSTANCE_TYPE =
registry.getNativeFunctionType(JSTypeNative.FUNCTION_INSTANCE_TYPE);
FUNCTION_PROTOTYPE =
registry.getNativeObjectType(JSTypeNative.FUNCTION_PROTOTYPE);
GREATEST_FUNCTION_TYPE =
registry.getNative
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>Type(JSTypeNative.GREATEST_FUNCTION_TYPE);
LEAST_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.LEAST_FUNCTION_TYPE);
NULL_TYPE =
registry.getNativeType(JSTypeNative.NULL_TYPE);
NUMBER_OBJECT_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE);
NUMBER_OBJECT_TYPE =
registry.getNativeObjectType(JSTypeNative.NUMBER_OBJECT_TYPE);
NUMBER_STRING_BOOLEAN =
registry.getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN);
NUMBER_TYPE =
registry.getNativeType(JSTypeNative.NUMBER_TYPE);
OBJECT_FUNCTION_TYPE =
registry.getNativeFunctionType(JSTypeNative.OBJECT_FUNCTION_TYPE);
OBJECT_NUMBER_STRING =
registry.getNativeType(JSTypeNative.OBJECT_NUMBER_STRING);
OBJECT_NUMBER_STRING_BOOLEAN =
registry.getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN);
OBJECT_PROTOTYPE =
registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE);
OBJECT_TYPE =
registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE);
RANGE_ERROR_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.RANGE_ERROR_FUNCTION_TYPE);
RANGE_ERROR_TYPE =
registry.getNativeObjectType(JSTypeNative.RANGE_ERROR_TYPE);
REFERENCE_ERROR_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.REFERENCE_ERROR_FUNCTION_TYPE);
REFERENCE_ERROR_TYPE =
registry.getNativeObjectType(JSTypeNative.REFERENCE_ERROR_TYPE);
REGEXP_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.REGEXP_FUNCTION_TYPE);
REGEXP_TYPE =
registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE);
STRING_OBJECT_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.STRING_OBJECT_FUNCTION_TYPE);
STRING_OBJECT_TYPE =
registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE);
STRING_TYPE =
registry.getNativeType(JSTypeNative.STRING_TYPE);
SYNTAX_ERROR_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.SYNTAX_ERROR_FUNCTION_TYPE);
SYNTAX_ERROR_TYPE =
registry.getNativeObjectType(JSTypeNative.SYNTAX_ERROR_TYPE);
TYPE_ERROR_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.TYPE_ERROR_FUNCTION_TYPE);
TYPE_ERROR_TYPE =
registry.getNativeObjectType(JSTypeNative.TYPE_ERROR_TYPE);
U2U_CONSTRUCTOR_TYPE =
registry.getNativeFunctionType(JSTypeNative.U2U_CONSTRUCTOR_TYPE);
U2U_FUNCTION_TYPE =
registry.getNativeFunctionType(JSTypeNative.U2U_FUNCTION_TYPE);
UNKNOWN_TYPE =
registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE);
URI_ERROR_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.URI_ERROR_FUNCTION_TYPE);
URI_ERROR_TYPE =
registry.getNativeObjectType(JSTypeNative.URI_ERROR_TYPE);
VOID_TYPE =
registry.getNativeType(JSTypeNative.VOID_TYPE);
addNativeProperties(registry);
NATIVE_PROPERTIES_COUNT = OBJECT_TYPE.getPropertiesCount();
}
/** Adds a basic set of properties to the native types. */
public static void addNativeProperties(JSTypeRegistry registry) {
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
JSType booleanType = registry.getNativeType(JSTypeNative.BOOLEAN_TYPE);
JSType numberType = registry.getNativeType(JSTypeNative.NUMBER_TYPE);
JSType stringType = registry.getNativeType(JSTypeNative.STRING_TYPE);
JSType unknownType = registry.getNativeType(JSTypeNative.UNKNOWN_TYPE);
ObjectType objectType =
registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE);
ObjectType arrayType =
registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE);
ObjectType dateType =
registry.getNativeObjectType(JSTypeNative.DATE_TYPE);
ObjectType regexpType =
registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE);
ObjectType booleanObjectType =
registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE);
ObjectType numberObjectType =
registry.getNativeObjectType(JSTypeNative.NUMBER_OBJECT_TYPE);
ObjectType stringObjectType =
registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE);
ObjectType objectPrototype = registry
.getNativeFunctionType(JSTypeNative.OBJECT_FUNCTION_TYPE)
.getPrototype();
addMethod(registry, objectPrototype, "constructor", objectType);
addMethod(registry, objectPrototype, "toString", stringType);
addMethod(registry, objectPrototype, "toLocaleString", stringType);
addMethod(registry, objectPrototype, "valueOf", unknownType);
addMethod(registry, objectPrototype, "hasOwnProperty", booleanType);
addMethod(registry, objectPrototype, "isPrototypeOf", booleanType);
addMethod(registry, objectPrototype, "propertyIsEnumerable", booleanType);
ObjectType arrayPrototype = registry
.getNativeFunctionType(JSTypeNative.ARRAY_FUNCTION_TYPE)
.getPrototype();
addMethod(registry, arrayPrototype, "constructor", arrayType);
addMethod(registry, arrayPrototype, "toString", stringType);
addMethod(registry, arrayPrototype, "toLocaleString", stringType);
addMethod(registry, arrayPrototype, "concat", arrayType);
addMethod(registry, arrayPrototype, "join", stringType);
addMethod(registry, arrayPrototype, "pop", unknownType);
addMethod(registry, arrayPrototype, "push", numberType);
addMethod(registry, arrayPrototype, "reverse", arrayType);
addMethod(registry, arrayPrototype, "shift", unknownType);
addMethod(registry, arrayPrototype, "slice", arrayType);
addMethod(registry, arrayPrototype, "sort", arrayType);
addMethod(registry, arrayPrototype, "splice", arrayType);
addMethod(registry, arrayPrototype, "unshift", numberType);
arrayType.defineDeclaredProperty("length", numberType, true, null);
ObjectType booleanPrototype = registry
.getNativeFunctionType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE)
.getPrototype();
addMethod(registry, booleanPrototype, "constructor", booleanObjectType);
addMethod(registry, booleanPrototype, "toString", stringType);
addMethod(registry, booleanPrototype, "valueOf", booleanType);
ObjectType datePrototype = registry
.getNativeFunctionType(JSTypeNative.DATE_FUNCTION_TYPE)
.getPrototype();
addMethod(registry, datePrototype, "constructor", dateType);
addMethod(registry, datePrototype, "toString", stringType);
addMethod(registry, datePrototype, "toDateString", stringType);
addMethod(registry, datePrototype, "toTimeString", stringType);
addMethod(registry, datePrototype, "toLocaleString", stringType);
addMethod(registry, datePrototype, "toLocaleDateString", stringType);
addMethod(registry, datePrototype, "toLocaleTimeString", string
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>Type);
addMethod(registry, datePrototype, "valueOf", numberType);
addMethod(registry, datePrototype, "getTime", numberType);
addMethod(registry, datePrototype, "getFullYear", numberType);
addMethod(registry, datePrototype, "getUTCFullYear", numberType);
addMethod(registry, datePrototype, "getMonth", numberType);
addMethod(registry, datePrototype, "getUTCMonth", numberType);
addMethod(registry, datePrototype, "getDate", numberType);
addMethod(registry, datePrototype, "getUTCDate", numberType);
addMethod(registry, datePrototype, "getDay", numberType);
addMethod(registry, datePrototype, "getUTCDay", numberType);
addMethod(registry, datePrototype, "getHours", numberType);
addMethod(registry, datePrototype, "getUTCHours", numberType);
addMethod(registry, datePrototype, "getMinutes", numberType);
addMethod(registry, datePrototype, "getUTCMinutes", numberType);
addMethod(registry, datePrototype, "getSeconds", numberType);
addMethod(registry, datePrototype, "getUTCSeconds", numberType);
addMethod(registry, datePrototype, "getMilliseconds", numberType);
addMethod(registry, datePrototype, "getUTCMilliseconds", numberType);
addMethod(registry, datePrototype, "getTimezoneOffset", numberType);
addMethod(registry, datePrototype, "setTime", numberType);
addMethod(registry, datePrototype, "setMilliseconds", numberType);
addMethod(registry, datePrototype, "setUTCMilliseconds", numberType);
addMethod(registry, datePrototype, "setSeconds", numberType);
addMethod(registry, datePrototype, "setUTCSeconds", numberType);
addMethod(registry, datePrototype, "setMinutes", numberType);
addMethod(registry, datePrototype, "setUTCMinutes", numberType);
addMethod(registry, datePrototype, "setHours", numberType);
addMethod(registry, datePrototype, "setUTCHours", numberType);
addMethod(registry, datePrototype, "setDate", numberType);
addMethod(registry, datePrototype, "setUTCDate", numberType);
addMethod(registry, datePrototype, "setMonth", numberType);
addMethod(registry, datePrototype, "setUTCMonth", numberType);
addMethod(registry, datePrototype, "setFullYear", numberType);
addMethod(registry, datePrototype, "setUTCFullYear", numberType);
addMethod(registry, datePrototype, "toUTCString", stringType);
addMethod(registry, datePrototype, "toGMTString", stringType);
ObjectType numberPrototype = registry
.getNativeFunctionType(JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE)
.getPrototype();
addMethod(registry, numberPrototype, "constructor", numberObjectType);
addMethod(registry, numberPrototype, "toString", stringType);
addMethod(registry, numberPrototype, "toLocaleString", stringType);
addMethod(registry, numberPrototype, "valueOf", numberType);
addMethod(registry, numberPrototype, "toFixed", stringType);
addMethod(registry, numberPrototype, "toExponential", stringType);
addMethod(registry, numberPrototype, "toPrecision", stringType);
ObjectType regexpPrototype = registry
.getNativeFunctionType(JSTypeNative.REGEXP_FUNCTION_TYPE)
.getPrototype();
addMethod(registry, regexpPrototype, "constructor", regexpType);
addMethod(registry, regexpPrototype, "exec",
registry.createNullableType(arrayType));
addMethod(registry, regexpPrototype, "test
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>", booleanType);
addMethod(registry, regexpPrototype, "toString", stringType);
regexpType.defineDeclaredProperty("source", stringType, true, null);
regexpType.defineDeclaredProperty("global", booleanType, true, null);
regexpType.defineDeclaredProperty("ignoreCase", booleanType, true, null);
regexpType.defineDeclaredProperty("multiline", booleanType, true, null);
regexpType.defineDeclaredProperty("lastIndex", numberType, true, null);
ObjectType stringPrototype = registry
.getNativeFunctionType(JSTypeNative.STRING_OBJECT_FUNCTION_TYPE)
.getPrototype();
addMethod(registry, stringPrototype, "constructor", stringObjectType);
addMethod(registry, stringPrototype, "toString", stringType);
addMethod(registry, stringPrototype, "valueOf", stringType);
addMethod(registry, stringPrototype, "charAt", stringType);
addMethod(registry, stringPrototype, "charCodeAt", numberType);
addMethod(registry, stringPrototype, "concat", stringType);
addMethod(registry, stringPrototype, "indexOf", numberType);
addMethod(registry, stringPrototype, "lastIndexOf", numberType);
addMethod(registry, stringPrototype, "localeCompare", numberType);
addMethod(registry, stringPrototype, "match",
registry.createNullableType(arrayType));
addMethod(registry, stringPrototype, "replace", stringType);
addMethod(registry, stringPrototype, "search", numberType);
addMethod(registry, stringPrototype, "slice", stringType);
addMethod(registry, stringPrototype, "split", arrayType);
addMethod(registry, stringPrototype, "substring", stringType);
addMethod(registry, stringPrototype, "toLowerCase", stringType);
addMethod(registry, stringPrototype, "toLocaleLowerCase", stringType);
addMethod(registry, stringPrototype, "toUpperCase", stringType);
addMethod(registry, stringPrototype, "toLocaleUpperCase", stringType);
stringObjectType.defineDeclaredProperty("length", numberType, true, null);
}
private static void addMethod(
JSTypeRegistry registry, ObjectType receivingType, String methodName,
JSType returnType) {
receivingType.defineDeclaredProperty(methodName,
new FunctionBuilder(registry).withReturnType(returnType).build(), true, null);
}
protected JSType createUnionType(JSType... variants) {
return registry.createUnionType(variants);
}
protected RecordTypeBuilder createRecordTypeBuilder() {
return new RecordTypeBuilder(registry);
}
protected JSType createNullableType(JSType type) {
return registry.createNullableType(type);
}
protected JSType createOptionalType(JSType type) {
return registry.createOptionalType(type);
}
/**
* Asserts that a Node representing a type expression resolves to the
* correct {@code JSType}.
*/
protected void assertTypeEquals(JSType expected, Node actual) {
assertTypeEquals(expected, new JSTypeExpression(actual, ""));
}
/**
* Asserts that a a type expression resolves to the correct {@code JSType}.
*/
protected void assertTypeEquals(JSType expected, JSTypeExpression actual) {
assertEquals(expected, resolve(actual));
}
/**
* Resolves a type expression, expecting the given warnings.
*/
protected JSType resolve(JSTypeExpression n, String... warnings) {
errorReporter.setWarnings(warnings);
return n.evaluate(null, registry);
}
/**
* A definition of all extern types. This should be kept in sync with
* javascript/externs/es3.js. This is used to check that the builtin types
* declared in {@link JSTypeRegistry
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import com.google.javascript.rhino.testing.BaseJSTypeTestCase;
abstract class CompilerTypeTestCase extends BaseJSTypeTestCase {
static final String ACTIVE_X_OBJECT_DEF =
"/**\n" +
" * @param {string} progId\n" +
" * @param {string=} opt_location\n" +
" * @constructor\n" +
" * @see http://msdn.microsoft.com/en-us/library/7sw4ddf8.aspx\n" +
" */\n" +
"function ActiveXObject(progId, opt_location) {}\n";
static final String CLOSURE_DEFS =
"var goog = {};" +
"goog.inherits = function(x, y) {};" +
"goog.abstractMethod = function() {};" +
"goog.isArray = function(x) {};" +
"goog.isDef = function(x) {};" +
"goog.isFunction = function(x) {};" +
"goog.isNull = function(x) {};" +
"goog.isString = function(x) {};" +
"goog.isObject = function(x) {};" +
"goog.isDefAndNotNull = function(x) {};";
/** A default set of externs for testing. */
static final String DEFAULT_EXTERNS =
"/** @constructor \n * @param {*} var_args */ " +
"function Function(var_args) {}" +
"/** @type {!Function} */ Function.prototype.apply;" +
"/** @type {!Function} */ Function.prototype.call;" +
"/** @constructor \n * @param {*} arg \n @return {string} */" +
"function String(arg) {}" +
"/** @type {number} */ String.prototype.length;" +
"/** @constructor \n * @param {*} var_args \n @return {!Array} */" +
"function Array(var_args) {}" +
"/** @type {number} */ Array.prototype.length;" + ACTIVE_X_OBJECT_DEF;
protected Compiler compiler;
protected CompilerOptions getOptions() {
CompilerOptions options = new CompilerOptions();
options.languageIn = LanguageMode.ECMASCRIPT5;
options.setWarningLevel(
DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.WARNING);
options.setCodingConvention(getCodingConvention());
return options;
}
protected CodingConvention getCodingConvention() {
return new GoogleCodingConvention();
}
@Override
protected void setUp() throws Exception {
compiler = new Compiler();
compiler.initOptions(getOptions());
registry = compiler.getTypeRegistry();
initTypes();
}
}
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
*/
@VisibleForTesting
Scope createInitialScope(Node root) {
NodeTraversal.traverse(
compiler, root, new DiscoverEnumsAndTypedefs(typeRegistry));
Scope s = new Scope(root, compiler);
declareNativeFunctionType(s, ARRAY_FUNCTION_TYPE);
declareNativeFunctionType(s, BOOLEAN_OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, DATE_FUNCTION_TYPE);
declareNativeFunctionType(s, ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, EVAL_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, FUNCTION_FUNCTION_TYPE);
declareNativeFunctionType(s, NUMBER_OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, RANGE_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, REFERENCE_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, REGEXP_FUNCTION_TYPE);
declareNativeFunctionType(s, STRING_OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, SYNTAX_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, TYPE_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, URI_ERROR_FUNCTION_TYPE);
declareNativeValueType(s, "undefined", VOID_TYPE);
// The typedef construct needs the any type, so that it can be assigned
// to anything. This is kind of a hack, and an artifact of the typedef
// syntax we've chosen.
declareNativeValueType(s, LEGACY_TYPEDEF, NO_TYPE);
// ActiveXObject is unqiuely special, because it can be used to construct
// any type (the type that it creates is related to the arguments you
// pass to it).
declareNativeValueType(s, "ActiveXObject", NO_OBJECT_TYPE);
return s;
}
private void declareNativeFunctionType(Scope scope, JSTypeNative tId) {
FunctionType t = typeRegistry.getNativeFunctionType(tId);
declareNativeType(scope, t.getInstanceType().getReferenceName(), t);
declareNativeType(
scope, t.getPrototype().getReferenceName(), t.getPrototype());
}
private void declareNativeValueType(Scope scope, String name,
JSTypeNative tId) {
declareNativeType(scope, name, typeRegistry.getNativeType(tId));
}
private void declareNativeType(Scope scope, String name, JSType t) {
scope.declare(name, null, t, null, false);
}
private static class DiscoverEnumsAndTypedefs
extends AbstractShallowStatementCallback {
private final JSTypeRegistry registry;
DiscoverEnumsAndTypedefs(JSTypeRegistry registry) {
this.registry = registry;
}
@Override
public void visit(NodeTraversal t, Node node, Node parent) {
Node nameNode = null;
switch (node.getType()) {
case Token.VAR:
for (Node child = node.getFirstChild();
child != null; child = child.getNext()) {
identifyNameNode(
child, child.getFirstChild(),
NodeUtil.getInfoForNameNode(child));
}
break;
case Token.EXPR_RESULT:
Node firstChild = node.getFirstChild();
if (firstChild.getType() == Token.ASSIGN) {
identifyNameNode(
firstChild.getFirstChild(), firstChild.getLastChild(),
firstChild.getJSDocInfo());
} else {
identifyNameNode(
firstChild, null, firstChild.getJSDocInfo());
}
break;
}
}
private void identifyNameNode(
Node nameNode, Node valueNode, JSDocInfo info) {
if (nameNode
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>.isQualifiedName()) {
if (info != null) {
if (info.hasEnumParameterType()) {
registry.identifyNonNullableName(nameNode.getQualifiedName());
} else if (info.hasTypedefType()) {
registry.identifyNonNullableName(nameNode.getQualifiedName());
}
}
if (valueNode != null &&
LEGACY_TYPEDEF.equals(valueNode.getQualifiedName())) {
registry.identifyNonNullableName(nameNode.getQualifiedName());
}
}
}
}
/**
* Given a node, determines whether that node names a prototype
* property, and if so, returns the qualified name node representing
* the owner of that property. Otherwise, returns null.
*/
private static Node getPrototypePropertyOwner(Node n) {
if (n.getType() == Token.GETPROP) {
Node firstChild = n.getFirstChild();
if (firstChild.getType() == Token.GETPROP &&
firstChild.getLastChild().getString().equals("prototype")) {
Node maybeOwner = firstChild.getFirstChild();
if (maybeOwner.isQualifiedName()) {
return maybeOwner;
}
}
}
return null;
}
private JSType getNativeType(JSTypeNative nativeType) {
return typeRegistry.getNativeType(nativeType);
}
private abstract class AbstractScopeBuilder
implements NodeTraversal.Callback {
/**
* The scope that we're builidng.
*/
final Scope scope;
private final List<DeferredSetType> deferredSetTypes =
Lists.newArrayList();
/**
* Functions that we found in the global scope and not in externs.
*/
private final List<Node> nonExternFunctions = Lists.newArrayList();
/**
* Type-less stubs.
*
* If at the end of traversal, we still don't have types for these
* stubs, then we should declare UNKNOWN types.
*/
private final List<StubDeclaration> stubDeclarations =
Lists.newArrayList();
/**
* The current source file that we're in.
*/
private String sourceName = null;
private AbstractScopeBuilder(Scope scope) {
this.scope = scope;
}
void setDeferredType(Node node, JSType type) {
deferredSetTypes.add(new DeferredSetType(node, type));
}
void resolveTypes() {
// Resolve types and attach them to nodes.
for (DeferredSetType deferred : deferredSetTypes) {
deferred.resolve(scope);
}
// Resolve types and attach them to scope slots.
Iterator<Var> vars = scope.getVars();
while (vars.hasNext()) {
vars.next().resolveType(typeParsingErrorReporter);
}
// Tell the type registry that any remaining types
// are unknown.
typeRegistry.resolveTypesInScope(scope);
}
@Override
public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
if (n.getType() == Token.FUNCTION ||
n.getType() == Token.SCRIPT) {
sourceName = (String) n.getProp(Node.SOURCENAME_PROP);
}
// We do want to traverse the name of a named function, but we don't
// want to traverse the arguments or body.
return parent == null || parent.getType() != Token.FUNCTION ||
n == parent.getFirstChild() || parent == scope.getRootNode();
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
attachLiteralTypes(t, n);
switch (n.getType()) {
case Token.CALL:
checkForClassDefiningCalls(t, n, parent);
break;
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> // who declare "global" names in an anonymous namespace.
Scope scopeToDeclareIn = scope;
if (n.getType() == Token.GETPROP && !scope.isGlobal() &&
isQnameRootedInGlobalScope(n)) {
Scope globalScope = scope.getGlobalScope();
// don't try to declare in the global scope if there's
// already a symbol there with this name.
if (!globalScope.isDeclared(variableName, false)) {
scopeToDeclareIn = scope.getGlobalScope();
}
}
// declared in closest scope?
if (scopeToDeclareIn.isDeclared(variableName, false)) {
Var oldVar = scopeToDeclareIn.getVar(variableName);
validator.expectUndeclaredVariable(
sourceName, n, parent, oldVar, variableName, type);
} else {
if (!inferred) {
setDeferredType(n, type);
}
CompilerInput input = compiler.getInput(sourceName);
boolean isExtern = input.isExtern();
Var newVar =
scopeToDeclareIn.declare(variableName, n, type, input, inferred);
if (shouldDeclareOnGlobalThis) {
ObjectType globalThis =
typeRegistry.getNativeObjectType(JSTypeNative.GLOBAL_THIS);
if (inferred) {
globalThis.defineInferredProperty(variableName,
type == null ?
getNativeType(JSTypeNative.NO_TYPE) :
type,
isExtern, n);
} else {
globalThis.defineDeclaredProperty(variableName, type, isExtern, n);
}
}
if (type instanceof EnumType) {
Node initialValue = newVar.getInitialValue();
boolean isValidValue = initialValue != null &&
(initialValue.getType() == Token.OBJECTLIT ||
initialValue.isQualifiedName());
if (!isValidValue) {
compiler.report(JSError.make(sourceName, n, ENUM_INITIALIZER));
}
}
// We need to do some additional work for constructors and interfaces.
if (type instanceof FunctionType &&
// We don't want to look at empty function types.
!type.isEmptyType()) {
FunctionType fnType = (FunctionType) type;
if ((fnType.isConstructor() || fnType.isInterface()) &&
!fnType.equals(getNativeType(U2U_CONSTRUCTOR_TYPE))) {
// Declare var.prototype in the scope chain.
FunctionType superClassCtor = fnType.getSuperClassConstructor();
scopeToDeclareIn.declare(variableName + ".prototype", n,
fnType.getPrototype(), input,
/* declared iff there's an explicit supertype */
superClassCtor == null ||
superClassCtor.getInstanceType().equals(
getNativeType(OBJECT_TYPE)));
// Make sure the variable is initialized to something if
// it constructs itself.
if (newVar.getInitialValue() == null &&
!isExtern &&
// We want to make sure that when we declare a new instance
// type (with @constructor) that there's actually a ctor for it.
// This doesn't apply to structural constructors
// (like function(new:Array). Checking the constructed
// type against the variable name is a sufficient check for
// this.
variableName.equals(
fnType.getInstanceType().getReferenceName())) {
compiler.report(
JSError.make(sourceName, n,
fnType.isConstructor() ?
CTOR_INITIALIZER : IFACE_INITIALIZER,
variableName));
}
}
}
}
}
/**
* Check if the given node is a property of a name in the global scope.
*/
private
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>.
*/
private final Kind kind;
/**
* The type of {@code this} in the scope of this function.
*/
private ObjectType typeOfThis;
/**
* The function node which this type represents. It may be {@code null}.
*/
private Node source;
/**
* The interfaces directly implemented by this function.
* It is only relevant for constructors. May not be {@code null}.
*/
private List<ObjectType> implementedInterfaces = ImmutableList.of();
/**
* The types which are subtypes of this function. It is only relevant for
* constructors and may be {@code null}.
*/
private List<FunctionType> subTypes;
/**
* The template type name. May be {@code null}.
*/
private String templateTypeName;
/** Creates an instance for a function that might be a constructor. */
FunctionType(JSTypeRegistry registry, String name, Node source,
ArrowType arrowType, ObjectType typeOfThis,
String templateTypeName, boolean isConstructor, boolean nativeType) {
super(registry, name,
registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE),
nativeType);
Preconditions.checkArgument(source == null ||
Token.FUNCTION == source.getType());
Preconditions.checkNotNull(arrowType);
this.source = source;
this.kind = isConstructor ? Kind.CONSTRUCTOR : Kind.ORDINARY;
if (isConstructor) {
this.typeOfThis = typeOfThis != null ?
typeOfThis : new InstanceObjectType(registry, this, nativeType);
} else {
this.typeOfThis = typeOfThis != null ?
typeOfThis :
registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE);
}
this.call = arrowType;
this.templateTypeName = templateTypeName;
}
/** Creates an instance for a function that is an interface. */
private FunctionType(JSTypeRegistry registry, String name, Node source) {
super(registry, name,
registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE));
Preconditions.checkArgument(source == null ||
Token.FUNCTION == source.getType());
Preconditions.checkArgument(name != null);
this.source = source;
this.call = new ArrowType(registry, new Node(Token.LP), null);
this.kind = Kind.INTERFACE;
this.typeOfThis = new InstanceObjectType(registry, this);
}
/** Creates an instance for a function that is an interface. */
static FunctionType forInterface(
JSTypeRegistry registry, String name, Node source) {
return new FunctionType(registry, name, source);
}
@Override
public boolean isInstanceType() {
// The universal constructor is its own instance, bizarrely.
return isEquivalentTo(registry.getNativeType(U2U_CONSTRUCTOR_TYPE));
}
@Override
public boolean isConstructor() {
return kind == Kind.CONSTRUCTOR;
}
@Override
public boolean isInterface() {
return kind == Kind.INTERFACE;
}
@Override
public boolean isOrdinaryFunction() {
return kind == Kind.ORDINARY;
}
@Override
public boolean isFunctionType() {
return true;
}
@Override
public boolean canBeCalled() {
return true;
}
public Iterable<Node> getParameters() {
Node n = getParametersNode();
if (n != null) {
return n.children();
} else {
return Collections.emptySet();
}
}
/** Gets an LP node that contains all params. May be null. */
public Node getParametersNode() {
return call.parameters;
}
/** Gets the minimum number of arguments that this
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> function requires. */
public int getMinArguments() {
// NOTE(nicksantos): There are some native functions that have optional
// parameters before required parameters. This algorithm finds the position
// of the last required parameter.
int i = 0;
int min = 0;
for (Node n : getParameters()) {
i++;
if (!n.isOptionalArg() && !n.isVarArgs()) {
min = i;
}
}
return min;
}
/**
* Gets the maximum number of arguments that this function requires,
* or Integer.MAX_VALUE if this is a variable argument function.
*/
public int getMaxArguments() {
Node params = getParametersNode();
if (params != null) {
Node lastParam = params.getLastChild();
if (lastParam == null || !lastParam.isVarArgs()) {
return params.getChildCount();
}
}
return Integer.MAX_VALUE;
}
public JSType getReturnType() {
return call.returnType;
}
public boolean isReturnTypeInferred() {
return call.returnTypeInferred;
}
/** Gets the internal arrow type. For use by subclasses only. */
ArrowType getInternalArrowType() {
return call;
}
/**
* Gets the {@code prototype} property of this function type. This is
* equivalent to {@code (ObjectType) getPropertyType("prototype")}.
*/
public FunctionPrototypeType getPrototype() {
// lazy initialization of the prototype field
if (prototype == null) {
setPrototype(new FunctionPrototypeType(registry, this, null));
}
return prototype;
}
/**
* Sets the prototype, creating the prototype object from the given
* base type.
* @param baseType The base type.
*/
public void setPrototypeBasedOn(ObjectType baseType) {
if (prototype == null) {
setPrototype(
new FunctionPrototypeType(
registry, this, baseType, isNativeObjectType()));
} else {
prototype.setImplicitPrototype(baseType);
}
}
/**
* Sets the prototype.
* @param prototype the prototype. If this value is {@code null} it will
* silently be discarded.
*/
public boolean setPrototype(FunctionPrototypeType prototype) {
if (prototype == null) {
return false;
}
// getInstanceType fails if the function is not a constructor
if (isConstructor() && prototype == getInstanceType()) {
return false;
}
boolean replacedPrototype = prototype != null;
this.prototype = prototype;
if (isConstructor() || isInterface()) {
FunctionType superClass = getSuperClassConstructor();
if (superClass != null) {
superClass.addSubType(this);
}
}
if (replacedPrototype) {
clearCachedValues();
}
return true;
}
/**
* Returns all interfaces implemented by a class or its superclass and any
* superclasses for any of those interfaces. If this is called before all
* types are resolved, it may return an incomplete set.
*/
public Iterable<ObjectType> getAllImplementedInterfaces() {
// Store them in a linked hash set, so that the compile job is
// deterministic.
Set<ObjectType> interfaces = Sets.newLinkedHashSet();
for (ObjectType type : getImplementedInterfaces()) {
addRelatedInterfaces(type, interfaces);
}
return interfaces;
}
private void addRelatedInterfaces(ObjectType instance, Set<ObjectType> set) {
FunctionType constructor = instance.getConstructor();
if (constructor != null) {
if (!constructor.isInterface()) {
return;
}
set.add(instance);
if (constructor.getSuperClassConstructor() != null) {
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
addRelatedInterfaces(
constructor.getSuperClassConstructor().getInstanceType(), set);
}
}
}
/** Returns interfaces implemented directly by a class or its superclass. */
public Iterable<ObjectType> getImplementedInterfaces() {
FunctionType superCtor = isConstructor() ?
getSuperClassConstructor() : null;
if (superCtor == null) {
return implementedInterfaces;
} else {
return Iterables.concat(
implementedInterfaces, superCtor.getImplementedInterfaces());
}
}
public void setImplementedInterfaces(List<ObjectType> implementedInterfaces) {
// Records this type for each implemented interface.
for (ObjectType type : implementedInterfaces) {
registry.registerTypeImplementingInterface(this, type);
}
this.implementedInterfaces = ImmutableList.copyOf(implementedInterfaces);
}
@Override
public boolean hasProperty(String name) {
return super.hasProperty(name) || "prototype".equals(name);
}
@Override
public boolean hasOwnProperty(String name) {
return super.hasOwnProperty(name) || "prototype".equals(name);
}
@Override
public JSType getPropertyType(String name) {
if ("prototype".equals(name)) {
return getPrototype();
} else {
if (!hasOwnProperty(name)) {
if ("call".equals(name)) {
// Define the "call" function lazily.
Node params = getParametersNode();
if (params == null) {
// If there's no params array, don't do any type-checking
// in this CALL function.
defineDeclaredProperty(name,
new FunctionBuilder(registry)
.withReturnType(getReturnType())
.build(),
false, source);
} else {
params = params.cloneTree();
Node thisTypeNode = Node.newString(Token.NAME, "thisType");
thisTypeNode.setJSType(
registry.createOptionalNullableType(getTypeOfThis()));
params.addChildToFront(thisTypeNode);
thisTypeNode.setOptionalArg(true);
defineDeclaredProperty(name,
new FunctionBuilder(registry)
.withParamsNode(params)
.withReturnType(getReturnType())
.build(),
false, source);
}
} else if ("apply".equals(name)) {
// Define the "apply" function lazily.
FunctionParamBuilder builder = new FunctionParamBuilder(registry);
// Ecma-262 says that apply's second argument must be an Array
// or an arguments object. We don't model the arguments object,
// so let's just be forgiving for now.
// TODO(nicksantos): Model the Arguments object.
builder.addOptionalParams(
registry.createNullableType(getTypeOfThis()),
registry.createNullableType(
registry.getNativeType(JSTypeNative.OBJECT_TYPE)));
defineDeclaredProperty(name,
new FunctionBuilder(registry)
.withParams(builder)
.withReturnType(getReturnType())
.build(),
false, source);
}
}
return super.getPropertyType(name);
}
}
@Override
boolean defineProperty(String name, JSType type,
boolean inferred, boolean inExterns, Node propertyNode) {
if ("prototype".equals(name)) {
ObjectType objType = type.toObjectType();
if (objType != null) {
if (objType.isEquivalentTo(prototype)) {
return true;
}
return setPrototype(
new FunctionPrototypeType(
registry, this, objType, isNativeObjectType()));
} else {
return false;
}
}
return super.defineProperty(name, type, inferred, inExterns, propertyNode);
}
@Override
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS>
public boolean isPropertyTypeInferred(String property) {
return "prototype".equals(property) ||
super.isPropertyTypeInferred(property);
}
@Override
public JSType getLeastSupertype(JSType that) {
return supAndInfHelper(that, true);
}
@Override
public JSType getGreatestSubtype(JSType that) {
return supAndInfHelper(that, false);
}
/**
* Computes the supremum or infimum of functions with other types.
* Because sup() and inf() share a lot of logic for functions, we use
* a single helper.
* @param leastSuper If true, compute the supremum of {@code this} with
* {@code that}. Otherwise compute the infimum.
* @return The least supertype or greatest subtype.
*/
private JSType supAndInfHelper(JSType that, boolean leastSuper) {
// NOTE(nicksantos): When we remove the unknown type, the function types
// form a lattice with the universal constructor at the top of the lattice,
// and the LEAST_FUNCTION_TYPE type at the bottom of the lattice.
//
// When we introduce the unknown type, it's much more difficult to make
// heads or tails of the partial ordering of types, because there's no
// clear hierarchy between the different components (parameter types and
// return types) in the ArrowType.
//
// Rather than make the situation more complicated by introducing new
// types (like unions of functions), we just fallback on the simpler
// approach of getting things right at the top and the bottom of the
// lattice.
if (isFunctionType() && that.isFunctionType()) {
if (isEquivalentTo(that)) {
return this;
}
FunctionType other = null;
if (that instanceof FunctionType) {
other = (FunctionType) that;
}
// If these are ordinary functions, then merge them.
// Don't do this if any of the params/return
// values are unknown, because then there will be cycles in
// their local lattice and they will merge in weird ways.
if (other != null &&
isOrdinaryFunction() && that.isOrdinaryFunction() &&
!this.call.hasUnknownParamsOrReturn() &&
!other.call.hasUnknownParamsOrReturn()) {
// Check for the degenerate case, but double check
// that there's not a cycle.
boolean isSubtypeOfThat = this.isSubtype(that);
boolean isSubtypeOfThis = that.isSubtype(this);
if (isSubtypeOfThat && !isSubtypeOfThis) {
return leastSuper ? that : this;
} else if (isSubtypeOfThis && !isSubtypeOfThat) {
return leastSuper ? this : that;
}
// Merge the two functions component-wise.
FunctionType merged = tryMergeFunctionPiecewise(other, leastSuper);
if (merged != null) {
return merged;
}
}
// The function instance type is a special case
// that lives above the rest of the lattice.
JSType functionInstance = registry.getNativeType(
JSTypeNative.FUNCTION_INSTANCE_TYPE);
if (functionInstance.isEquivalentTo(that)) {
return leastSuper ? that : this;
} else if (functionInstance.isEquivalentTo(this)) {
return leastSuper ? this : that;
}
// In theory, we should be using the GREATEST_FUNCTION_TYPE as the
// greatest function. In practice, we don't because it's way too
// broad. The greatest function takes var_args None parameters, which
// means that all parameters register a type warning.
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> //
// Instead, we use the U2U ctor type, which has unknown type args.
FunctionType greatestFn =
registry.getNativeFunctionType(JSTypeNative.U2U_CONSTRUCTOR_TYPE);
FunctionType leastFn =
registry.getNativeFunctionType(JSTypeNative.LEAST_FUNCTION_TYPE);
return leastSuper ? greatestFn : leastFn;
}
return leastSuper ?
super.getLeastSupertype(that) :
super.getGreatestSubtype(that);
}
/**
* Try to get the sup/inf of two functions by looking at the
* piecewise components.
*/
private FunctionType tryMergeFunctionPiecewise(
FunctionType other, boolean leastSuper) {
Node newParamsNode = null;
if (call.hasEqualParameters(other.call)) {
newParamsNode = call.parameters;
} else {
// If the parameters are not equal, don't try to merge them.
// Someday, we should try to merge the individual params.
return null;
}
JSType newReturnType = leastSuper ?
call.returnType.getLeastSupertype(other.call.returnType) :
call.returnType.getGreatestSubtype(other.call.returnType);
ObjectType newTypeOfThis = null;
if (isEquivalent(typeOfThis, other.typeOfThis)) {
newTypeOfThis = typeOfThis;
} else {
JSType maybeNewTypeOfThis = leastSuper ?
typeOfThis.getLeastSupertype(other.typeOfThis) :
typeOfThis.getGreatestSubtype(other.typeOfThis);
if (maybeNewTypeOfThis instanceof ObjectType) {
newTypeOfThis = (ObjectType) maybeNewTypeOfThis;
} else {
newTypeOfThis = leastSuper ?
registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE) :
registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE);
}
}
boolean newReturnTypeInferred =
call.returnTypeInferred || other.call.returnTypeInferred;
return new FunctionType(
registry, null, null,
new ArrowType(
registry, newParamsNode, newReturnType, newReturnTypeInferred),
newTypeOfThis, null, false, false);
}
/**
* Given a constructor or an interface type, get its superclass constructor
* or {@code null} if none exists.
*/
public FunctionType getSuperClassConstructor() {
Preconditions.checkArgument(isConstructor() || isInterface());
ObjectType maybeSuperInstanceType = getPrototype().getImplicitPrototype();
if (maybeSuperInstanceType == null) {
return null;
}
return maybeSuperInstanceType.getConstructor();
}
/**
* Given a constructor or an interface type and a property, finds the
* top-most superclass that has the property defined (including this
* constructor).
*/
public JSType getTopMostDefiningType(String propertyName) {
Preconditions.checkState(isConstructor() || isInterface());
Preconditions.checkArgument(getPrototype().hasProperty(propertyName));
FunctionType ctor = this;
JSType topInstanceType;
do {
topInstanceType = ctor.getInstanceType();
ctor = ctor.getSuperClassConstructor();
} while (ctor != null && ctor.getPrototype().hasProperty(propertyName));
return topInstanceType;
}
/**
* Two function types are equal if their signatures match. Since they don't
* have signatures, two interfaces are equal if their names match.
*/
@Override
public boolean isEquivalentTo(JSType otherType) {
if (!(otherType instanceof FunctionType)) {
return false;
}
FunctionType that = (FunctionType) otherType;
if (!that.isFunctionType()) {
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> return false;
}
if (this.isConstructor()) {
if (that.isConstructor()) {
return this == that;
}
return false;
}
if (this.isInterface()) {
if (that.isInterface()) {
return this.getReferenceName().equals(that.getReferenceName());
}
return false;
}
if (that.isInterface()) {
return false;
}
return this.typeOfThis.isEquivalentTo(that.typeOfThis) &&
this.call.isEquivalentTo(that.call);
}
@Override
public int hashCode() {
return isInterface() ? getReferenceName().hashCode() : call.hashCode();
}
public boolean hasEqualCallType(FunctionType otherType) {
return this.call.isEquivalentTo(otherType.call);
}
/**
* Informally, a function is represented by
* {@code function (params): returnType} where the {@code params} is a comma
* separated list of types, the first one being a special
* {@code this:T} if the function expects a known type for {@code this}.
*/
@Override
public String toString() {
if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) {
return "Function";
}
StringBuilder b = new StringBuilder(32);
b.append("function (");
int paramNum = call.parameters.getChildCount();
boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType();
if (hasKnownTypeOfThis) {
if (isConstructor()) {
b.append("new:");
} else {
b.append("this:");
}
b.append(typeOfThis.toString());
}
if (paramNum > 0) {
if (hasKnownTypeOfThis) {
b.append(", ");
}
Node p = call.parameters.getFirstChild();
if (p.isVarArgs()) {
appendVarArgsString(b, p.getJSType());
} else {
b.append(p.getJSType().toString());
}
p = p.getNext();
while (p != null) {
b.append(", ");
if (p.isVarArgs()) {
appendVarArgsString(b, p.getJSType());
} else {
b.append(p.getJSType().toString());
}
p = p.getNext();
}
}
b.append("): ");
b.append(call.returnType);
return b.toString();
}
/** Gets the string representation of a var args param. */
private void appendVarArgsString(StringBuilder builder, JSType paramType) {
if (paramType.isUnionType()) {
// Remove the optionalness from the var arg.
paramType = ((UnionType) paramType).getRestrictedUnion(
registry.getNativeType(JSTypeNative.VOID_TYPE));
}
builder.append("...[").append(paramType.toString()).append("]");
}
/**
* A function is a subtype of another if their call methods are related via
* subtyping and {@code this} is a subtype of {@code that} with regard to
* the prototype chain.
*/
@Override
public boolean isSubtype(JSType that) {
if (JSType.isSubtype(this, that)) {
return true;
}
if (that.isFunctionType()) {
if (((FunctionType) that).isInterface()) {
// Any function can be assigned to an interface function.
return true;
}
if (this.isInterface()) {
// An interface function cannot be assigned to anything.
return false;
}
// If
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> functionA is a subtype of functionB, then their "this" types
// should be contravariant. However, this causes problems because
// of the way we enforce overrides. Because function(this:SubFoo)
// is not a subtype of function(this:Foo), our override check treats
// this as an error. It also screws up out standard method
// for aliasing constructors. Let's punt on all this for now.
// TODO(nicksantos): fix this.
FunctionType other = (FunctionType) that;
boolean treatThisTypesAsCovariant =
// If either one of these is a ctor, skip 'this' checking.
this.isConstructor() || other.isConstructor() ||
// An interface 'this'-type is non-restrictive.
// In practical terms, if C implements I, and I has a method m,
// then any m doesn't necessarily have to C#m's 'this'
// type doesn't need to match I.
(other.typeOfThis.getConstructor() != null &&
other.typeOfThis.getConstructor().isInterface()) ||
// If one of the 'this' types is covariant of the other,
// then we'll treat them as covariant (see comment above).
other.typeOfThis.isSubtype(this.typeOfThis) ||
this.typeOfThis.isSubtype(other.typeOfThis);
return treatThisTypesAsCovariant && this.call.isSubtype(other.call);
}
return getNativeType(JSTypeNative.FUNCTION_PROTOTYPE).isSubtype(that);
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseFunctionType(this);
}
/**
* Gets the type of instance of this function.
* @throws IllegalStateException if this function is not a constructor
* (see {@link #isConstructor()}).
*/
public ObjectType getInstanceType() {
Preconditions.checkState(hasInstanceType());
return typeOfThis;
}
/** Sets the instance type. This should only be used for special native types. */
void setInstanceType(ObjectType instanceType) {
typeOfThis = instanceType;
}
/**
* Returns whether this function type has an instance type.
*/
public boolean hasInstanceType() {
return isConstructor() || isInterface();
}
/**
* Gets the type of {@code this} in this function.
*/
public ObjectType getTypeOfThis() {
return typeOfThis.isNoObjectType() ?
registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE) : typeOfThis;
}
/**
* Gets the source node or null if this is an unknown function.
*/
public Node getSource() {
return source;
}
/**
* Sets the source node.
*/
public void setSource(Node source) {
this.source = source;
}
/** Adds a type to the list of subtypes for this type. */
private void addSubType(FunctionType subType) {
if (subTypes == null) {
subTypes = Lists.newArrayList();
}
subTypes.add(subType);
}
@Override
void clearCachedValues() {
super.clearCachedValues();
if (subTypes != null) {
for (FunctionType subType : subTypes) {
subType.clearCachedValues();
}
}
if (!isNativeObjectType()) {
if (hasInstanceType()) {
getInstanceType().clearCachedValues();
}
if (prototype != null) {
prototype.clearCachedValues();
}
}
}
/**
* Returns a list of types that are subtypes of this type. This is only valid
Closure, 82
<FILEB>
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
<CHANGEE>
<FILEE>
<FILEB>
return displayName != null && !displayName.isEmpty();
}
/**
* If we see a type name without braces, it might be legacy jsdoc.
* So we shouldn't emit warnings about it. This method is how we skip
* those warnings.
*/
void forgiveUnknownNames() {}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
<CHANGES>
return isNoType() || isNoObjectType() || isNoResolvedType();
<CHANGEE>
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
public boolean isFunctionPrototypeType() {
return false;
}
public boolean isStringObjectType() {
<FILEE>
<SCANS> * for constructor functions, and may be null. This allows a downward
* traversal of the subtype graph.
*/
public List<FunctionType> getSubTypes() {
return subTypes;
}
@Override
public boolean hasCachedValues() {
return prototype != null || super.hasCachedValues();
}
/**
* Gets the template type name.
*/
public String getTemplateTypeName() {
return templateTypeName;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
setResolvedTypeInternal(this);
call = (ArrowType) safeResolve(call, t, scope);
prototype = (FunctionPrototypeType) safeResolve(prototype, t, scope);
// Warning about typeOfThis if it doesn't resolve to an ObjectType
// is handled further upstream.
//
// TODO(nicksantos): Handle this correctly if we have a UnionType.
//
// TODO(nicksantos): In ES3, the runtime coerces "null" to the global
// activation object. In ES5, it leaves it as null. Just punt on this
// issue for now by coercing out null. This is complicated by the
// fact that when most people write @this {Foo}, they really don't
// mean "nullable Foo". For certain tags (like @extends) we de-nullify
// the name for them.
JSType maybeTypeOfThis = safeResolve(typeOfThis, t, scope);
if (maybeTypeOfThis != null) {
maybeTypeOfThis = maybeTypeOfThis.restrictByNotNullOrUndefined();
}
if (maybeTypeOfThis instanceof ObjectType) {
typeOfThis = (ObjectType) maybeTypeOfThis;
}
boolean changed = false;
ImmutableList.Builder<ObjectType> resolvedInterfaces =
ImmutableList.builder();
for (ObjectType iface : implementedInterfaces) {
ObjectType resolvedIface = (ObjectType) iface.resolve(t, scope);
resolvedInterfaces.add(resolvedIface);
changed |= (resolvedIface != iface);
}
if (changed) {
implementedInterfaces = resolvedInterfaces.build();
}
if (subTypes != null) {
for (int i = 0; i < subTypes.size(); i++) {
subTypes.set(i, (FunctionType) subTypes.get(i).resolve(t, scope));
}
}
return super.resolveInternal(t, scope);
}
@Override
public String toDebugHashCodeString() {
if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) {
return super.toDebugHashCodeString();
}
StringBuilder b = new StringBuilder(32);
b.append("function (");
int paramNum = call.parameters.getChildCount();
boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType();
if (hasKnownTypeOfThis) {
b.append("this:");
b.append(getDebugHashCodeStringOf(typeOfThis));
}
if (paramNum > 0) {
if (hasKnownTypeOfThis) {
b.append(", ");
}
Node p = call.parameters.getFirstChild();
b.append(getDebugHashCodeStringOf(p.getJSType()));
p = p.getNext();
while (p != null) {
b.append(", ");
b.append(getDebugHashCodeStringOf(p.getJSType()));
p = p.getNext();
}
}
b.append(")");
b.append(": ");
b.append(getDebugHashCodeStringOf(call.returnType));
return b.toString();
}
private String getDebugHashCodeStringOf(JSType type) {
if (type == this